tree-wide: check memstream buffer after closing the handle

When closing the FILE handle attached to a memstream, it may attempt to
do a realloc() that may fail during OOM situations, in which case we are
left with the buffer pointer pointing to NULL and buffer size > 0. For
example:

```
    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>

    void *realloc(void *ptr, size_t size) {
        return NULL;
    }

    int main(int argc, char *argv[])
    {
        FILE *f;
        char *buf;
        size_t sz = 0;

        f = open_memstream(&buf, &sz);
        if (!f)
            return -ENOMEM;

        fputs("Hello", f);

        fflush(f);
        printf("buf: 0x%lx, sz: %lu, errno: %d\n",
                    (unsigned long) buf, sz, errno);
        fclose(f);
        printf("buf: 0x%lx, sz: %lu, errno: %d\n",
                    (unsigned long) buf, sz, errno);

        return 0;
    }
```

```
$ gcc -o main main.c
$ ./main
buf: 0x74d4a0, sz: 5, errno: 0
buf: 0x0, sz: 5, errno: 0
```

This might do unexpected things if the underlying code expects a valid
pointer to the memstream buffer after closing the handle.

Found by Nallocfuzz.
This commit is contained in:
Frantisek Sumsal
2023-05-24 21:59:10 +02:00
parent 08a8fd6e8d
commit f392dfb5a1
17 changed files with 132 additions and 70 deletions
+10 -7
View File
@@ -9,6 +9,7 @@
#include "alloc-util.h"
#include "escape.h"
#include "extract-word.h"
#include "fd-util.h"
#include "fileio.h"
#include "gunicode.h"
#include "locale-util.h"
@@ -603,9 +604,9 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
STATE_CSI,
STATE_CSO,
} state = STATE_OTHER;
char *obuf = NULL;
_cleanup_free_ char *obuf = NULL;
_cleanup_fclose_ FILE *f = NULL;
size_t osz = 0, isz, shift[2] = {}, n_carriage_returns = 0;
FILE *f;
assert(ibuf);
assert(*ibuf);
@@ -713,11 +714,13 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
}
}
if (fflush_and_check(f) < 0) {
fclose(f);
return mfree(obuf);
}
fclose(f);
if (fflush_and_check(f) < 0)
return NULL;
f = safe_fclose(f);
if (!obuf)
return NULL;
free_and_replace(*ibuf, obuf);