Skip to content

False positive with use of longjmp in an external library and FORTIFY_SOURCE #721

@Lekensteyn

Description

@Lekensteyn

According to the FAQ, -D_FORTIFY_SOURCE is not completely supported (see also #247). False negatives are not ideal, but understandable.

However, I ran into a different problem. My application (built with ASAN) somehow reports a seemingly arbitrary stack-buffer-overflow. It turns out that a third-party (non-ASAN instrumented Lua) library used longjmp (actually _longjmp). Normally this will unpoison the stack, but because -D_FORTIFY_SOURCE was used for that application, it got replaced by __longjmp_chk (a glibc function).

The __longjmp_chk function does not invoke longjmp and is therefore missed by the interceptors for longjmp. This prevents the redzones on the stack from being unpoisoned. Subsequent use of this stack address by an ASAN-instrumented function causes a false stack-buffer-overflow report.

On Arch Linux all packages are built with -D_FORTIFY_SOURCE by default.

Suggestion

Add interceptor for __longjmp_chk. If there is support for this I could also try to prepare a patch for this.

Reproducer

Conditions:

  • External library used setjmp and calls ASAN-instrumented function.
  • ASAN-instrumented function invokes the external library again which triggers longjmp.
  • External library passes its stack address (overlapping with the redzone) to ASAN-instrumented code (interceptor).

Implementation:

user.c

#include <stdio.h>
#include <string.h>

extern void external_callme(void (*callback)(void));
extern void external_check(void);
extern void external_stack_user(void);

static void setmsg(char *msg)
{
    strcpy(msg, "never called");
    puts(msg);
}

static void callback(void)
{
    /* Note: this triggers addition of a redzone. */
    char msg[16];
    external_check();

    /* assumption: due to longjmp, this part is never called */
    setmsg(msg);
}

int main()
{
    external_callme(callback);
    external_stack_user();
    return 0;
}

external.c

#include <setjmp.h>
#include <stdio.h>
#include <string.h>

jmp_buf jenv;

void external_callme(void (*callback)(void))
{
    if (setjmp(jenv) == 0) {
        puts("callback");
        callback();
        puts("callback returned?!");
    } else {
        puts("callback longjumped");
    }
}

void external_check(void)
{
    puts("longjmp");
    longjmp(jenv, 1);
}

void external_stack_user(void)
{
    char buf[128] = "";
    char buf2[128] = "";

    /* This will trigger the ASAN interceptor which detects an invalid stack address. */
    if (!memcmp(buf, buf2, sizeof(buf))) {
        puts("bogus");
    }
}

Makefile

CFLAGS := -g -O2
# Important: triggers use of __longjmp_chk
CFLAGS_external += -D_FORTIFY_SOURCE=2

check: user
    LD_LIBRARY_PATH=. ./user

user: user.c libexternal.so
    $(CC) $(CFLAGS) -fsanitize=address -o $@ $< -L. -lexternal

libexternal.so: external.c
    $(CC) $(CFLAGS) $(CFLAGS_external) -o $@ $< -shared -fPIC

clean:
    rm -f user libexternal.so

.PHONY: clean check

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions