From 6eae4256794d1f40cecef9f4ef422f34ce3113ee Mon Sep 17 00:00:00 2001 From: apocelipes Date: Mon, 4 Dec 2023 16:47:00 +0900 Subject: [PATCH] fix(Memory): fix reading procfs files and simplify data parsing Files in procfs are always changed by kernel. It means if we read the data with more than one read syscall, we could get an incorrect data which was broken by kernel. A typical case about this problem was reported by psutil in https://github.com/giampaolo/psutil/issues/2050 Using an big buffer let read reads the whole file can fix this. psutil uses a 32K buffer while procps-ng uses a 8K buffer, i think a 8K buffer is enough. --- src/common/io/io.h | 4 +++ src/detection/memory/memory_linux.c | 53 +++++++++++++---------------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/common/io/io.h b/src/common/io/io.h index d2519e156..c311feb76 100644 --- a/src/common/io/io.h +++ b/src/common/io/io.h @@ -11,6 +11,10 @@ #include #include typedef int FFNativeFD; + // procfs's file can be changed between read calls such as /proc/meminfo and /proc/uptime. + // one safe way to read correct data is reading the whole file in a single read syscall + // 8192 comes from procps-ng: https://gitlab.com/procps-ng/procps/-/blob/master/library/meminfo.c?ref_type=heads#L39 + #define PROC_FILE_BUFFSIZ 8192 #endif static inline FFNativeFD FFUnixFD2NativeFD(int unixfd) diff --git a/src/detection/memory/memory_linux.c b/src/detection/memory/memory_linux.c index 1932896b6..d4ebf4de9 100644 --- a/src/detection/memory/memory_linux.c +++ b/src/detection/memory/memory_linux.c @@ -6,12 +6,11 @@ const char* ffDetectMemory(FFMemoryResult* ram) { - FF_AUTO_CLOSE_FILE FILE* meminfo = fopen("/proc/meminfo", "r"); - if(meminfo == NULL) - return "Failed to open /proc/meminfo"; - - char* FF_AUTO_FREE line = NULL; - size_t len = 0; + char buf[PROC_FILE_BUFFSIZ]; + ssize_t nRead = ffReadFileData("/proc/meminfo", sizeof(buf) - 1, buf); + if(nRead < 0) + return "ffReadFileData(\"/proc/meminfo\", sizeof(buf)-1, buf)"; + buf[nRead] = '\0'; uint64_t memTotal = 0, shmem = 0, @@ -19,32 +18,26 @@ const char* ffDetectMemory(FFMemoryResult* ram) buffers = 0, cached = 0, sReclaimable = 0; - uint8_t count = 0; + + char *token = NULL; + if((token = strstr(buf, "MemTotal:")) != NULL) + sscanf(token, "MemTotal: %" PRIu64, &memTotal); - while (getline(&line, &len, meminfo) != EOF) - { - switch (line[0]) - { - case 'B': - if (sscanf(line, "Buffers: %" PRIu64, &buffers) > 0) - if (++count >= 6) goto done; - break; - case 'C': - if (sscanf(line, "Cached: %" PRIu64, &cached) > 0) - if (++count >= 6) goto done; - break; - case 'M': - if(sscanf(line, "MemTotal: %" PRIu64, &memTotal) > 0 || sscanf(line, "MemFree: %" PRIu64, &memFree) > 0) - if (++count >= 6) goto done; - break; - case 'S': - if(sscanf(line, "Shmem: %" PRIu64, &shmem) > 0 || sscanf(line, "SReclaimable: %" PRIu64, &sReclaimable) > 0) - if (++count >= 6) goto done; - break; - } - } + if((token = strstr(buf, "MemFree:")) != NULL) + sscanf(token, "MemFree: %" PRIu64, &memFree); + + if((token = strstr(buf, "Buffers:")) != NULL) + sscanf(token, "Buffers: %" PRIu64, &buffers); + + if((token = strstr(buf, "Cached:")) != NULL) + sscanf(token, "Cached: %" PRIu64, &cached); + + if((token = strstr(buf, "Shmem:")) != NULL) + sscanf(token, "Shmem: %" PRIu64, &shmem); + + if((token = strstr(buf, "SReclaimable:")) != NULL) + sscanf(token, "SReclaimable: %" PRIu64, &sReclaimable); -done: ram->bytesTotal = memTotal * 1024lu; ram->bytesUsed = (memTotal + shmem - memFree - buffers - cached - sReclaimable) * 1024lu;