Tag
Hash :
bd4ed1ca
Author :
Date :
2020-05-24T20:51:48
libpkgconf: fileio: prevent buffer overflow. pkgconf_fgetline is called with a user-defined buffer, its size, and a FILE stream to read input from. If the buffer is almost completely filled and the file stream contains an escaped character, then it is possible to trigger an off-by-one buffer overflow with a '\0' character. Easiest example to trigger this: char buf[2]; pkgconf_fgetline(buf, sizeof(buf), stdin); Enter "\\" (two backslashes) and press enter. If the library and the program are compiled with address sanitizer, you will see the program crashing. Otherwise it depends on your architecture what happens. Since nobody should be using a buffer of only size 1 or 2, keep enough space for a possibly escaped character in while loop by subtracting one more byte for this situation, not just for '\0'.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
/*
* fileio.c
* File reading utilities
*
* Copyright (c) 2012 pkgconf authors (see AUTHORS).
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#include <libpkgconf/stdinc.h>
#include <libpkgconf/libpkgconf.h>
char *
pkgconf_fgetline(char *line, size_t size, FILE *stream)
{
char *s = line;
char *end = line + size - 2;
bool quoted = false;
int c = '\0', c2;
if (s == NULL)
return NULL;
while (s < end && (c = getc(stream)) != EOF)
{
if (c == '\\' && !quoted)
{
quoted = true;
continue;
}
else if (c == '#')
{
if (!quoted) {
/* Skip the rest of the line */
do {
c = getc(stream);
} while (c != '\n' && c != EOF);
*s++ = c;
break;
}
quoted = false;
continue;
}
else if (c == '\n')
{
if (quoted)
{
/* Trim spaces */
do {
c2 = getc(stream);
} while (c2 == '\t' || c2 == ' ');
ungetc(c2, stream);
quoted = false;
continue;
}
else
{
*s++ = c;
}
break;
}
else if (c == '\r')
{
*s++ = '\n';
if ((c2 = getc(stream)) == '\n')
{
if (quoted)
{
quoted = false;
continue;
}
break;
}
ungetc(c2, stream);
if (quoted)
{
quoted = false;
continue;
}
break;
}
else
{
if (quoted) {
*s++ = '\\';
quoted = false;
}
*s++ = c;
}
}
if (c == EOF && (s == line || ferror(stream)))
return NULL;
*s = '\0';
/* Remove newline character. */
if (s > line && *(--s) == '\n') {
*s = '\0';
if (s > line && *(--s) == '\r')
*s = '\0';
}
return line;
}