Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix several stack buffer overflows and pointer overflows #103

Merged
merged 2 commits into from
Feb 19, 2024

Conversation

skeeto
Copy link
Contributor

@skeeto skeeto commented Feb 17, 2024

Several overflows may occur in switch case '[' when the input pattern contains many escaped characters. The added backslashes leave too little space in the output pattern when processing nested brackets such that the remaining input length exceeds the output capacity. Therefore all these concatenations must also be checked.

The ADD_CHAR was missed in 41281ea (#87). The switch can exit exactly at capacity, leaving no room for the finishing '$', causing an overflow.

In STRING_CAT, the end pointer is positioned one past the end of the destination, and it is undefined behavior to compute an address beyond the end pointer, including for comparisons, even temporarily. The UB occurs exactly when buffer overflow would have occurred, so the buffer overflow check could be optimized away by compilers. Even if this wasn't the case, the check could produce a false negative if the computed address overflowed the address space, which is, after all, why the C standard doesn't define behavior in the first place.

The fix is simple: Check using sizes, not addresses. The explicit cast suppresses warnings around signed-unsigned comparisons. It would also be worthwhile to assert(end > p), which would have caught some of these issues sooner, but there is no established assertion discipline and I didn't want to introduce it here.


These issues were discovered with this quick-and-dirty afl fuzz target:

#define _GNU_SOURCE
#define PCRE2_CODE_UNIT_WIDTH 8
#define UNIX
#define EC_VERSION_SUFFIX ""
#define EC_VERSION_MAJOR 0
#define EC_VERSION_MINOR 0
#define EC_VERSION_PATCH 0
#include "src/lib/ec_glob.c"
#include "src/lib/editorconfig.c"
#include "src/lib/ini.c"
#include "src/lib/misc.c"
#include <unistd.h>
#include <sys/mman.h>

__AFL_FUZZ_INIT();

int main(void)
{
    __AFL_INIT();
    int fd = memfd_create("fuzz", 0);
    unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF;
    while (__AFL_LOOP(10000)) {
        int len = __AFL_FUZZ_TESTCASE_LEN;
        ftruncate(fd, 0);
        write(fd, buf, len);
        struct editorconfig_handle ec = {0};
        ec.conf_file_name = (char[]){'0'+fd, 0};
        editorconfig_parse("/proc/self/fd/", &ec);
    }
}

The interface only accepts input through a file path, which makes testing a bit more complex. memfd_create() avoids writing fuzz input through a real file and allows for parallel fuzzing because the virtual file path is private. I built and ran it like so:

$ echo >include/config.h
$ afl-gcc-fast -Iinclude -g3 -fsanitize=address,undefined fuzz.c -lpcre2-8
$ afl-fuzz -i tests/parser/comments.in -o fuzzout ./a.out

overflow-input.txt is an input that triggers all overflows addressed by these commits.

Copy link
Member

@xuhdev xuhdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution and detailed commit message! The PR looks good, only two minor comments.

...the end pointer is positioned one past the end of the destination, and it is undefined behavior to compute an address beyond the end pointer...

  • Do you have a reference to this?
  • Is using the address one past the end of an array defined behavior?

src/lib/ec_glob.c Show resolved Hide resolved
src/lib/ec_glob.c Show resolved Hide resolved
Several overflows may occur in switch case '[' when the input pattern
contains many escaped characters. The added backslashes leave too little
space in the output pattern when processing nested brackets such that
the remaining input length exceeds the output capacity. Therefore all
these concatenations must also be checked.

The ADD_CHAR was missed in 41281ea (editorconfig#87). The switch can exit exactly at
capacity, leaving no room for the finishing '$', causing an overflow.

These overflows were discovered through fuzz testing with afl.
The end pointer is positioned one past the end of the destination, and
it is undefined behavior to compute an address beyond the end pointer,
including for comparisons, even temporarily. The UB occurs exactly when
buffer overflow would have occurred, so the buffer overflow check could
be optimized away by compilers. Even if this wasn't the case, the check
could produce a false negative if the computed address overflowed the
address space, which is, after all, why the C standard doesn't define
behavior in the first place.

The fix is simple: Check using sizes, not addresses. The explicit cast
suppresses warnings about signed-unsigned comparisons, and the assertion
checks the cast.
@skeeto
Copy link
Contributor Author

skeeto commented Feb 18, 2024 via email

Copy link
Member

@xuhdev xuhdev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the detailed explanation!

@xuhdev xuhdev merged commit 4d5518a into editorconfig:master Feb 19, 2024
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants