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

ASAN runtime SEGVs after a recursive glob() #1654

Open
whathappensif opened this issue May 19, 2023 · 4 comments
Open

ASAN runtime SEGVs after a recursive glob() #1654

whathappensif opened this issue May 19, 2023 · 4 comments

Comments

@whathappensif
Copy link

The address sanitizer runtime SEGVs after a recursive glob(). I needn’t.

This is because after calling the real glob and swapping things back, it does an unconditional
pglob_copy = 0;
When the outer (intercepted) glob later tries to dereference pglob_copy, it will SEGV near address 0, in our environment at 0x28 in wrapped_gl_readdir trying to get the caller’s readdir function.
It should instead capture the previous value and restore it only if it set it.

Why do we do recursive glob()? We use the GLOB_ALTDIRFUNC feature, and in the opendir function (HDFS, though that doesn’t really matter), it constructs a URL and invokes curl routines on it. Curl eventually wanders into this backtrace which I feel comfortable sharing:

#0 0x00002b2ffda38cd4 in glob () from libasan.so.4
#1 0x00002b2ffd7c9615 in loadConfigFiles () from libgssapi_krb5.so.2
#2 0x00002b2ffd7c967f in updateMechList () from libgssapi_krb5.so.2
#3 0x00002b2ffd7c9199 in build_mechSet () from libgssapi_krb5.so.2
#4 0x00002b2ffd7c90ae in gss_indicate_mechs () from libgssapi_krb5.so.2
#5 0x00002b2ffd7d05ae in gss_indicate_mechs_by_attrs () from libgssapi_krb5.so.2
#6 0x00002b2ffd7f865b in get_available_mechs () from libgssapi_krb5.so.2
#7 0x00002b2ffd7f8854 in get_negotiable_mechs () from libgssapi_krb5.so.2
#8 0x00002b2ffd7f4e73 in init_ctx_new () from libgssapi_krb5.so.2
#9 0x00002b2ffd7f57bd in spnego_gss_init_sec_context () from libgssapi_krb5.so.2
#10 0x00002b2ffd7c8a3a in gss_init_sec_context () from libgssapi_krb5.so.2

It’s not that we’re trying to do a recursive glob(), it’s that our code calls HDFS stuff which calls CURL which calls GSSAPI_KRB5 which wants to glob configuration files:
(gdb) x/s $rdi
0x2b2ffd7b08b8: "…/etc/gss/mech.d/*.conf"

@ramosian-glider
Copy link
Member

Any chance you can point to the source and provide some reproduction instructions?

@whathappensif
Copy link
Author

Unfortunately, not. As hinted at the end of my posting

It’s not that we’re trying to do a recursive glob(), it’s that our code calls HDFS stuff which calls CURL which calls GSSAPI_KRB5 which wants to glob configuration files: (gdb) x/s $rdi 0x2b2ffd7b08b8: "…/etc/gss/mech.d/*.conf"

so it is pretty buried deep within our product's usage. When I read the code in advance of submitting the bug, it was clear that setting pglob_copy = 0; unconditionally wouldn't work during recursion; it needs to save/restore.

@johnwheffner
Copy link

This reproduces it for me with glibc 2.39-0ubuntu8.3, both using gcc 13.3.0-6ubuntu2~24.04 and clang 18.1.3 (1ubuntu1). (There must exist some files under the filesystem root.)

#include <dirent.h>
#include <glob.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>

static void *opendir_calling_glob(const char *name)
{
    glob_t globbuf;
    glob("/foo", 0, NULL, &globbuf);
    globfree(&globbuf);
    return opendir(name);
}

static void asan_recursive_glob_crash_repro()
{
    glob_t globbuf;
    globbuf.gl_opendir = opendir_calling_glob;
    globbuf.gl_readdir = (void *(*)(void*))readdir;
    globbuf.gl_closedir = (void (*)(void *))closedir;
    globbuf.gl_stat = (int (*)(const char *, void *))stat;
    globbuf.gl_lstat = (int (*)(const char *, void *))lstat;

    glob("/*", GLOB_ALTDIRFUNC, NULL, &globbuf);
    globfree(&globbuf);
}

int main()
{
    asan_recursive_glob_crash_repro();
    return EXIT_SUCCESS;
}
$ clang -g -fsanitize=address -fno-omit-frame-pointer -o test main.c
$ ./test
AddressSanitizer:DEADLYSIGNAL
=================================================================
==3768813==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000028 (pc 0x76e6d0a5439a bp 0x7fffd9c72470 sp 0x7fffd9c72460 T0)
==3768813==The signal is caused by a READ memory access.
==3768813==Hint: address points to the zero page.
    #0 0x76e6d0a5439a in wrapped_gl_readdir ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:2316
    #1 0x76e6d06f6809 in glob_in_dir ../posix/glob.c:1370
    #2 0x76e6d06f756f in __glob ../posix/glob.c:1132
    #3 0x76e6d0ad41b1 in glob ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:2358
    #4 0x557f78276597 in asan_recursive_glob_crash_repro /home/jheffner/src/asan_glob_repro/main.c:24
    #5 0x557f78276626 in main /home/jheffner/src/asan_glob_repro/main.c:30
    #6 0x76e6d062a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
    #7 0x76e6d062a28a in __libc_start_main_impl ../csu/libc-start.c:360
    #8 0x557f782761a4 in _start (/data/jheffner/src/asan_glob_repro/test+0x11a4) (BuildId: 14d5e959f85dbe9c8b4fd5f88992d9a5ff53cdfd)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:2316 in wrapped_gl_readdir
==3768813==ABORTING

@whathappensif
Copy link
Author

Thanks, John. (Keeping your hands dirty, I see...)

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

No branches or pull requests

3 participants