From 216109b8ed5dafa7c241f5309655ba6d86a9ba38 Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Wed, 5 Aug 2026 17:37:35 +0300 Subject: [PATCH 01/10] fix(PIString): initialize toChar() return value and fix operator+= OOB - toChar(): char v was uninitialized, causing UB when sscanf fails to match (empty string). Initialize to 0. - operator+=(PIConstChars): loop iterated l < d.size() instead of l < str.size(), reading past the end of str after d.enlarge(). This is a heap buffer overread with undefined behavior. --- libs/main/text/pistring.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/main/text/pistring.cpp b/libs/main/text/pistring.cpp index db3b9da8..65bf8035 100644 --- a/libs/main/text/pistring.cpp +++ b/libs/main/text/pistring.cpp @@ -671,7 +671,7 @@ PIString & PIString::operator+=(const PIConstChars & str) { if (!str.isEmpty()) { size_t os = d.size(); d.enlarge(str.size()); - for (size_t l = 0; l < d.size(); ++l) { + for (size_t l = 0; l < str.size(); ++l) { d[os + l] = str[l]; } } @@ -1763,7 +1763,7 @@ PIString PIString::toLowerCase() const { char PIString::toChar() const { - char v; + char v = 0; sscanf(dataAscii(), "%c", &v); return v; } From e89ae88f3a975ebcc5af8b1d0621f80ba13cca46 Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Wed, 5 Aug 2026 17:37:57 +0300 Subject: [PATCH 02/10] fix(PICAN): align socket handling with PIEthernet patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Sentinel: sock = 0 → sock = -1 (0 is stdin, valid fd) - openDevice(): close socket on ioctl/bind failure (resource leak) - openDevice(): set O_NONBLOCK via fcntl after socket() - openDevice(): check setsockopt() return value - closeDevice(): call shutdown() before close(), reset sock to -1 - readDevice() / writeDevice(): guard against sock == -1 - Add missing #include --- libs/main/io_devices/pican.cpp | 21 ++++++++++++++++++--- libs/main/io_devices/pican.h | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/libs/main/io_devices/pican.cpp b/libs/main/io_devices/pican.cpp index 2d6fe04e..6d1e2295 100644 --- a/libs/main/io_devices/pican.cpp +++ b/libs/main/io_devices/pican.cpp @@ -24,6 +24,7 @@ # define PIP_CAN #endif #ifdef PIP_CAN +# include # include # include # include @@ -49,7 +50,7 @@ PICAN::PICAN(const PIString & path, PIIODevice::DeviceMode mode): PIIODevice(pat setThreadedReadBufferSize(256); setPath(path); can_id = 0; - sock = 0; + sock = -1; PRIVATE->event.create(); } @@ -67,19 +68,25 @@ bool PICAN::openDevice() { sock = socket(PF_CAN, SOCK_RAW, CAN_RAW); if (sock < 0) { piCoutObj << "Error! while opening socket"; + sock = -1; return false; } + fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK); ifreq ifr; strcpy(ifr.ifr_name, path().dataAscii()); piCout << "PICAN try to get interface index..."; if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) { piCoutObj << "Error! while determin the interface ioctl"; + ::close(sock); + sock = -1; return false; } struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; - setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv); + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char *)&tv, sizeof tv) < 0) { + piCoutObj << "Error! while setting socket receive timeout"; + } // bind socket to all CAN interface sockaddr_can addr; addr.can_family = AF_CAN; @@ -87,6 +94,8 @@ bool PICAN::openDevice() { piCout << "PICAN try to bind socket to interface" << ifr.ifr_ifindex; if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { piCoutObj << "Error! while binding socket"; + ::close(sock); + sock = -1; return false; } piCout << "PICAN Open OK!"; @@ -101,7 +110,11 @@ bool PICAN::openDevice() { bool PICAN::closeDevice() { #ifdef PIP_CAN interrupt(); - if (sock > 0) ::close(sock); + if (sock != -1) { + ::shutdown(sock, SHUT_RDWR); + ::close(sock); + sock = -1; + } #endif return true; } @@ -109,6 +122,7 @@ bool PICAN::closeDevice() { ssize_t PICAN::readDevice(void * read_to, ssize_t max_size) { #ifdef PIP_CAN + if (sock == -1) return -1; // piCout << "PICAN read"; can_frame frame; ssize_t ret = 0; @@ -127,6 +141,7 @@ ssize_t PICAN::readDevice(void * read_to, ssize_t max_size) { ssize_t PICAN::writeDevice(const void * data, ssize_t max_size) { #ifdef PIP_CAN + if (sock == -1) return -1; // piCout << "PICAN write" << can_id << max_size; if (max_size > 8) { piCoutObj << "Can't send CAN frame bigger than 8 bytes (requested " << max_size << ")!"; diff --git a/libs/main/io_devices/pican.h b/libs/main/io_devices/pican.h index 93bef921..37938818 100644 --- a/libs/main/io_devices/pican.h +++ b/libs/main/io_devices/pican.h @@ -73,7 +73,7 @@ protected: private: PRIVATE_DECLARATION(PIP_EXPORT) - int sock; + int sock = -1; int can_id, readed_id; }; From 6c5c4b73e0bae8b0a26201ebdd875bc62aab97cb Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Wed, 5 Aug 2026 17:38:01 +0300 Subject: [PATCH 03/10] fix(PIClientServer): delete PIEthernet when client_factory returns nullptr When the custom client_factory() returned nullptr, the incoming PIEthernet pointer was leaked. Matches existing pattern used in the max_clients overflow branch. --- libs/client_server/piclientserver_server.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/client_server/piclientserver_server.cpp b/libs/client_server/piclientserver_server.cpp index edfd2083..aaea5d21 100644 --- a/libs/client_server/piclientserver_server.cpp +++ b/libs/client_server/piclientserver_server.cpp @@ -37,6 +37,7 @@ PIClientServer::Server::Server() { auto sc = client_factory(); if (!sc) { piCout << "ClientFactory returns nullptr!"_tr("PIClientServer"); + delete c; return; } sc->createForServer(this, c); From e05abf16b5350e7ef2daf5d1a2fc283ca101e3c7 Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Wed, 5 Aug 2026 17:38:07 +0300 Subject: [PATCH 04/10] fix(PIWaitEvent): use -1 as pipe_fd sentinel instead of 0 File descriptor 0 is stdin (a valid fd). Using 0 as the 'not-open' sentinel caused isCreate() to return false and destroy() to skip close() when pipe() happened to allocate fds {0, 1} (e.g. in daemons with closed stdio). POSIX convention: -1 = invalid fd. --- libs/main/core/piwaitevent_p.cpp | 6 +++--- libs/main/core/piwaitevent_p.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/main/core/piwaitevent_p.cpp b/libs/main/core/piwaitevent_p.cpp index 47983a5d..36ec80bc 100644 --- a/libs/main/core/piwaitevent_p.cpp +++ b/libs/main/core/piwaitevent_p.cpp @@ -65,9 +65,9 @@ void PIWaitEvent::destroy() { } #else for (int i = 0; i < 2; ++i) { - if (pipe_fd[i] != 0) { + if (pipe_fd[i] != -1) { ::close(pipe_fd[i]); - pipe_fd[i] = 0; + pipe_fd[i] = -1; } } #endif @@ -140,7 +140,7 @@ bool PIWaitEvent::isCreate() const { #ifdef WINDOWS return event; #else - return pipe_fd[ReadEnd] != 0; + return pipe_fd[ReadEnd] != -1; #endif } diff --git a/libs/main/core/piwaitevent_p.h b/libs/main/core/piwaitevent_p.h index 9814d449..1af19ac1 100644 --- a/libs/main/core/piwaitevent_p.h +++ b/libs/main/core/piwaitevent_p.h @@ -55,7 +55,7 @@ private: #ifdef WINDOWS void * event = nullptr; #else - int pipe_fd[2] = {0, 0}; + int pipe_fd[2] = {-1, -1}; fd_set fds[3]; enum { ReadEnd = 0, From 9cd56ab66ceb1fd401cccc2814492587f738e192 Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Wed, 5 Aug 2026 17:38:12 +0300 Subject: [PATCH 05/10] fix(PIEthernet): check open() return value with >= 0 instead of != 0 open() returns -1 on error. The check fd != 0 treated -1 as a valid fd (since -1 != 0 is true), causing devctl(-1, ...) and close(-1) on QNX. Fixed to fd >= 0. --- libs/main/io_devices/piethernet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/main/io_devices/piethernet.cpp b/libs/main/io_devices/piethernet.cpp index ad4a08b3..a1245cc1 100644 --- a/libs/main/io_devices/piethernet.cpp +++ b/libs/main/io_devices/piethernet.cpp @@ -383,7 +383,7 @@ void PIEthernet::applyBuffers() { void PIEthernet::applyTimeout(int fd, int opt, PISystemTime tm) { if (fd == 0) return; - // piCoutObj << "setReadIsBlocking" << yes; + // piCoutObj << "setReadIsBlocking" << yes; #ifdef WINDOWS DWORD _tm = tm.toMilliseconds(); #else @@ -1234,7 +1234,7 @@ PIEthernet::InterfaceList PIEthernet::interfaces() { # ifdef QNX # ifndef BLACKBERRY int fd = ::open((PIString("/dev/io-net/") + ci.name).dataAscii(), O_RDONLY); - if (fd != 0) { + if (fd >= 0) { nic_config_t nic; devctl(fd, DCMD_IO_NET_GET_CONFIG, &nic, sizeof(nic), 0); ::close(fd); From b2e637ee2b4fde52f079c46db971f5d5eb29ea80 Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Wed, 5 Aug 2026 17:38:17 +0300 Subject: [PATCH 06/10] fix(PIIOByteArray): advance pos by actual bytes read, not requested size readDevice() did pos += size instead of pos += ret. The clamping on the next line (pos = min(pos, data_->size_s())) masked the issue in practice, but the semantics were wrong. Aligns with writeDevice() which correctly uses pos += rs.size_s(). --- libs/main/io_devices/piiobytearray.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/main/io_devices/piiobytearray.cpp b/libs/main/io_devices/piiobytearray.cpp index 3b6e8374..699bd1e9 100644 --- a/libs/main/io_devices/piiobytearray.cpp +++ b/libs/main/io_devices/piiobytearray.cpp @@ -62,8 +62,7 @@ ssize_t PIIOByteArray::readDevice(void * read_to, ssize_t size) { if (ret <= 0) return -1; memcpy(read_to, data_->data(pos), ret); // piCout << "readed" << ret; - pos += size; - if (pos > data_->size_s()) pos = data_->size_s(); + pos += ret; return ret; } From 13a9a75f79a97569d00a00b3ee395afabe7e5f9e Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Wed, 5 Aug 2026 17:38:22 +0300 Subject: [PATCH 07/10] fix(PISerial): break infinite loop when readDevice returns <= 0 In the blocking read path (timeout_ms <= 0), the while loop had no exit condition for readDevice() returning 0 (EOF) or -1 (error). This caused an infinite busy-loop at 100% CPU. Added 'else break' to exit the loop on read failure, matching the timeout branch which already handles this case. --- libs/main/io_devices/piserial.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libs/main/io_devices/piserial.cpp b/libs/main/io_devices/piserial.cpp index 9859ef68..401d5dfc 100644 --- a/libs/main/io_devices/piserial.cpp +++ b/libs/main/io_devices/piserial.cpp @@ -513,7 +513,10 @@ bool PISerial::read(void * data, int size, double timeout_ms) { all = readDevice(data, 1); while (all < size) { ret = readDevice(&((uchar *)data)[all], size - all); - if (ret > 0) all += ret; + if (ret > 0) + all += ret; + else + break; } setOption(BlockingRead, br); received(data, all); @@ -1235,8 +1238,8 @@ PIVector PISerial::availableDevicesInfo(bool test) { for (const auto & e: de) { // TODO changes in FileInfo for (const auto & p: prefixes) { if (e.name().startsWith(p)) { - di = DeviceInfo(); - di.path = e.path; + di = DeviceInfo(); + di.path = e.path; # ifdef LINUX ssize_t lsz = readlink(("/sys/class/tty/" + e.name()).dataAscii(), linkbuf, 1024); if (lsz > 0) { From 27a9f05d65f8b50c4340e604795c63e4bf907c18 Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Wed, 5 Aug 2026 17:38:28 +0300 Subject: [PATCH 08/10] fix(PIProcess): handle fork() failure (return value -1) fork() can return -1 on error (too many processes, out of memory). The old code treated pid_ == -1 as a successful fork, entering the parent branch and calling waitpid(-1, ...) which waits for ANY child process. Added explicit check: close pipes, free memory, and return on fork failure. --- libs/main/system/piprocess.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libs/main/system/piprocess.cpp b/libs/main/system/piprocess.cpp index ffa6a871..115b9a69 100644 --- a/libs/main/system/piprocess.cpp +++ b/libs/main/system/piprocess.cpp @@ -319,6 +319,13 @@ void PIProcess::startProc(bool detached) { auto largs = convertToCharArrays(args); auto lenv = convertToCharArrays(env); int pid_ = fork(); + if (pid_ < 0) { + piCoutObj << "\"fork\" error: " << errorString(); + PRIVATE->closeAllPipes(); + delete[] largs; + delete[] lenv; + return; + } if (!detached) PRIVATE->pid = pid_; if (pid_ == 0) { if (!wd.isEmpty()) { From 3f293ebd65514bc3dd03591d0596a14df976e377 Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Wed, 5 Aug 2026 17:38:32 +0300 Subject: [PATCH 09/10] fix(PIFileTransfer): validate file id before array access processFile() accessed files_[id - 1] without validating id. When id == 0 (from a crafted network packet), id - 1 = -1 caused out-of-bounds access. Added bounds check: id must be in range [1, files_.size()]. Stops receive and logs error on invalid id. --- libs/main/io_utils/pifiletransfer.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libs/main/io_utils/pifiletransfer.cpp b/libs/main/io_utils/pifiletransfer.cpp index 7389a769..acdeac73 100644 --- a/libs/main/io_utils/pifiletransfer.cpp +++ b/libs/main/io_utils/pifiletransfer.cpp @@ -131,6 +131,12 @@ bool PIFileTransfer::sendFiles(const PIVector & files) { void PIFileTransfer::processFile(int id, ullong start, PIByteArray & data) { // piCout << "processFile" << id << files_.size(); + if (id <= 0 || id > files_.size_s()) { + cur_file_string = "Error: Invalid file id " + PIString::fromNumber(id); + piCoutObj << cur_file_string; + stopReceive(); + return; + } PFTFileInfo fi = files_[id - 1]; bytes_file_all = fi.size; bytes_file_cur = start; From 1ddec15b874ca123ee384f5689d6f9ff9c2e3b39 Mon Sep 17 00:00:00 2001 From: "andrey.bychkov" Date: Wed, 5 Aug 2026 19:47:35 +0300 Subject: [PATCH 10/10] threadsafe fixes --- libs/main/io_devices/pipeer.cpp | 5 ++++- libs/mqtt_client/pimqttclient.cpp | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/libs/main/io_devices/pipeer.cpp b/libs/main/io_devices/pipeer.cpp index c121dfea..0cf8bd6a 100644 --- a/libs/main/io_devices/pipeer.cpp +++ b/libs/main/io_devices/pipeer.cpp @@ -623,7 +623,10 @@ bool PIPeer::dataRead(const uchar * readed, ssize_t size) { return true; } cnt++; - if (cnt > _PIPEER_MSG_TTL || from == dp->name) return true; + if (cnt > _PIPEER_MSG_TTL || from == dp->name) { + eth_mutex.unlock(); + return true; + } sba << type << from << to << cnt << pba; // piCout << "translate packet" << from << "->" << to << ", ttl =" << cnt; sendToNeighbour(dp, sba); diff --git a/libs/mqtt_client/pimqttclient.cpp b/libs/mqtt_client/pimqttclient.cpp index 60ffb958..133639e5 100644 --- a/libs/mqtt_client/pimqttclient.cpp +++ b/libs/mqtt_client/pimqttclient.cpp @@ -60,7 +60,7 @@ STATIC_INITIALIZER_END PRIVATE_DEFINITION_START(PIMQTT::Client) MQTTClient client = nullptr; - bool connected = false; + std::atomic connected{false}; PIProtectedVariable endpoints;