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