naccyde / rpms / systemd

Forked from rpms/systemd 11 months ago
Clone
6f381c
From a2ba34a79de3748f51d57541c54dbe22e1d03a9e Mon Sep 17 00:00:00 2001
6f381c
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
6f381c
Date: Fri, 4 Oct 2019 16:17:27 +0200
6f381c
Subject: [PATCH] pstore: rework memory handling for dmesg
6f381c
6f381c
Semmle Security Reports report:
6f381c
> The problem occurs on the way realloc is being used. When a size
6f381c
> bigger than the chunk that wants to be reallocated is passed, realloc
6f381c
> try to malloc a  bigger size, however in the case that malloc fails
6f381c
> (for example, by forcing a big allocation)  realloc will return NULL.
6f381c
>
6f381c
> According to the man page:
6f381c
> "The realloc() function returns a pointer to the newly allocated
6f381c
> memory, which is suitably aligned for any built-in type and may be
6f381c
> different from ptr,  or  NULL  if  the  request fails.   If size was
6f381c
> equal to 0, either NULL or a pointer suitable to be passed to free()
6f381c
> is returned.  If realloc() fails, the original block is left
6f381c
> untouched; it is  not  freed or moved."
6f381c
>
6f381c
> The problem occurs when the memory ptr passed to the first argument of
6f381c
> realloc is the same as the one used for the result, for example in
6f381c
> this case:
6f381c
>
6f381c
> dmesg = realloc(dmesg, dmesg_size + strlen(pe->dirent.d_name) +
6f381c
> strlen(":\n") + pe->content_size + 1);
6f381c
>
6f381c
> https://lgtm.com/projects/g/systemd/systemd/snapshot/f8bcb81955f9e93a4787627e28f43fffb2a84836/files/src/pstore/pstore.c?sort=name&dir=A
6f381c
> SC&mode=heatmap#L300
6f381c
>
6f381c
> If the malloc inside that realloc fails, then the original memory
6f381c
> chunk will never be free but since realloc will return NULL, the
6f381c
> pointer to that memory chunk will be lost and a memory leak will
6f381c
> occur.
6f381c
>
6f381c
> In case you are curious, this is the query we used to find this problem:
6f381c
> https://lgtm.com/query/8650323308193591473/
6f381c
6f381c
Let's use a more standard pattern: allocate memory using greedy_realloc, and
6f381c
instead of freeing it when we wrote out a chunk, let's just move the cursor
6f381c
back to the beginning and reuse the memory we allocated previously.
6f381c
6f381c
If we fail to allocate the memory for dmesg contents, don't write the dmesg
6f381c
entry, but let's still process the files to move them out of pstore.
6f381c
6f381c
(cherry picked from commit 8198c3e42b0614b6bd1db6f38813b842c8577304)
6f381c
6f381c
Related: #2158832
6f381c
---
6f381c
 src/pstore/pstore.c | 43 ++++++++++++++++++++++++++-----------------
6f381c
 1 file changed, 26 insertions(+), 17 deletions(-)
6f381c
6f381c
diff --git a/src/pstore/pstore.c b/src/pstore/pstore.c
6f381c
index 7353e83a81..d70e142b4d 100644
6f381c
--- a/src/pstore/pstore.c
6f381c
+++ b/src/pstore/pstore.c
6f381c
@@ -176,9 +176,11 @@ static int write_dmesg(const char *dmesg, size_t size, const char *id) {
6f381c
         ssize_t wr;
6f381c
         int r;
6f381c
 
6f381c
-        if (isempty(dmesg) || size == 0)
6f381c
+        if (size == 0)
6f381c
                 return 0;
6f381c
 
6f381c
+        assert(dmesg);
6f381c
+
6f381c
         /* log_info("Record ID %s", id); */
6f381c
 
6f381c
         ofd_path = path_join(arg_archivedir, id, "dmesg.txt");
6f381c
@@ -204,7 +206,8 @@ static int write_dmesg(const char *dmesg, size_t size, const char *id) {
6f381c
 static void process_dmesg_files(PStoreList *list) {
6f381c
         /* Move files, reconstruct dmesg.txt */
6f381c
         _cleanup_free_ char *dmesg = NULL, *dmesg_id = NULL;
6f381c
-        size_t dmesg_size = 0;
6f381c
+        size_t dmesg_size = 0, dmesg_allocated = 0;
6f381c
+        bool dmesg_bad = false;
6f381c
         PStoreEntry *pe;
6f381c
 
6f381c
         /* Handle each dmesg file: files processed in reverse
6f381c
@@ -281,33 +284,39 @@ static void process_dmesg_files(PStoreList *list) {
6f381c
                 /* Now move file from pstore to archive storage */
6f381c
                 move_file(pe, pe_id);
6f381c
 
6f381c
+                if (dmesg_bad)
6f381c
+                        continue;
6f381c
+
6f381c
                 /* If the current record id is NOT the same as the
6f381c
                  * previous record id, then start a new dmesg.txt file */
6f381c
-                if (!pe_id || !dmesg_id || !streq(pe_id, dmesg_id)) {
6f381c
+                if (!streq_ptr(pe_id, dmesg_id)) {
6f381c
                         /* Encountered a new dmesg group, close out old one, open new one */
6f381c
-                        if (dmesg) {
6f381c
-                                (void) write_dmesg(dmesg, dmesg_size, dmesg_id);
6f381c
-                                dmesg = mfree(dmesg);
6f381c
-                                dmesg_size = 0;
6f381c
-                        }
6f381c
+                        (void) write_dmesg(dmesg, dmesg_size, dmesg_id);
6f381c
+                        dmesg_size = 0;
6f381c
 
6f381c
                         /* now point dmesg_id to storage of pe_id */
6f381c
                         free_and_replace(dmesg_id, pe_id);
6f381c
                 }
6f381c
 
6f381c
-                /* Reconstruction of dmesg is done as a useful courtesy, do not log errors */
6f381c
-                dmesg = realloc(dmesg, dmesg_size + strlen(pe->dirent.d_name) + strlen(":\n") + pe->content_size + 1);
6f381c
-                if (dmesg) {
6f381c
-                        dmesg_size += sprintf(&dmesg[dmesg_size], "%s:\n", pe->dirent.d_name);
6f381c
-                        if (pe->content) {
6f381c
-                                memcpy(&dmesg[dmesg_size], pe->content, pe->content_size);
6f381c
-                                dmesg_size += pe->content_size;
6f381c
-                        }
6f381c
+                /* Reconstruction of dmesg is done as a useful courtesy: do not fail, but don't write garbled
6f381c
+                 * output either. */
6f381c
+                size_t needed = strlen(pe->dirent.d_name) + strlen(":\n") + pe->content_size + 1;
6f381c
+                if (!GREEDY_REALLOC(dmesg, dmesg_allocated, dmesg_size + needed)) {
6f381c
+                        log_warning_errno(ENOMEM, "Failed to write dmesg file: %m");
6f381c
+                        dmesg_bad = true;
6f381c
+                        continue;
6f381c
+                }
6f381c
+
6f381c
+                dmesg_size += sprintf(dmesg + dmesg_size, "%s:\n", pe->dirent.d_name);
6f381c
+                if (pe->content) {
6f381c
+                        memcpy(dmesg + dmesg_size, pe->content, pe->content_size);
6f381c
+                        dmesg_size += pe->content_size;
6f381c
                 }
6f381c
 
6f381c
                 pe_id = mfree(pe_id);
6f381c
         }
6f381c
-        if (dmesg)
6f381c
+
6f381c
+        if (!dmesg_bad)
6f381c
                 (void) write_dmesg(dmesg, dmesg_size, dmesg_id);
6f381c
 }
6f381c