Skip to content

False errors when compiling and running programs using AddressSanitizer on Lustre filesystem #3413

Description

@twsearle

Description

isdaoscontainer() passes negative getxattr/listxattr return directly to calloccalloc(1, SIZE_MAX). This is picked up by AddressSanitizer causing a false failure:

=================================================================
==1846880==ERROR: AddressSanitizer: requested allocation size 0xffffffffffffffff (0x800 after adjustments for alignment, red zones etc.) exceeds maximum supported size of 0x10000000000 (thread T0)
    #0 0x7f8f4475ce47 in __interceptor_calloc /.../libsanitizer/asan/asan_malloc_linux.cpp:77
    #1 0x7f8f3e4d4e7e in isdaoscontainer /.../src/libdispatch/dinfermodel.c:1633
    #2 0x7f8f3e4d4e7e in NC_infermodel /.../src/libdispatch/dinfermodel.c:1008

Component: libdispatch/dinfermodel.c (path/model inference on open)

Version: netCDF-C main (present since isdaoscontainer was added; not in 4.9.2)

Environment: Linux, HDF5 ≥ 1.12, HAVE_SYS_XATTR_H defined, files on a filesystem that carries extended attributes (e.g. Lustre). Symptom is fatal only under AddressSanitizer, but the bad allocation request happens on every affected open.

Description

isdaoscontainer() is called by NC_infermodel() on every nc_open()/nc_create()-read of an HDF5-accessible file. It walks the file's extended attributes and casts the ssize_t return of GETXATTR/LISTXATTR straight to size_t before calling calloc:

ssize_t xlen;
xlen = LISTXATTR(path, NULL, 0);
if (xlen > 0) {
    ...
    for (; p < endp; p += (strlen(p) + 1)) {
        if (strstr(p, ".daos") != NULL) { rc = 1; break; }
        xlen = GETXATTR(path, p, NULL, 0);              /* can return -1 */
        if ((xvalue = (char*)calloc(1, (size_t)xlen)) == NULL)  /* (size_t)(-1) == SIZE_MAX */
            { stat = NC_ENOMEM; goto done; }
        (void)GETXATTR(path, p, xvalue, xlen);
        ...

If getxattr(path, name, NULL, 0) returns -1 (e.g. EPERM/ENODATA for a trusted.*/system.* attribute as a non-privileged user, or a TOCTOU removal between listxattr and getxattr), (size_t)xlen becomes 0xffffffffffffffff and is passed to calloc.

Normally calloc just returns NULL and the function bails via the NC_ENOMEM branch, so it's silently benign. But under AddressSanitizer the oversized request aborts the process before calloc can return:

ERROR: AddressSanitizer: requested allocation size 0xffffffffffffffff
    #0 __interceptor_calloc
    #1 isdaoscontainer libdispatch/dinfermodel.c
    #2 NC_infermodel
    #3 NC_open
    #4 nc_open

Reproducer (illustrates the cast bug in isolation)

#include <stdlib.h>
#include <sys/types.h>

int main(void) {
    ssize_t xlen = -1;                 /* what getxattr returns on error */
    void *p = calloc(1, (size_t)xlen); /* requests SIZE_MAX bytes */
    return p ? 0 : 1;
}
cc -fsanitize=address repro.c && ./a.out
# => AddressSanitizer: requested allocation size 0xffffffffffffffff ... ABORTING

In situ, it reproduces by opening any HDF5/netCDF-4 file that carries an extended attribute whose getxattr(..., NULL, 0) returns -1, with the reader built -fsanitize=address.

Expected vs actual

  • Expected: an attribute whose size cannot be queried is skipped; open proceeds.
  • Actual: a calloc(1, SIZE_MAX) request that ASAN treats as fatal (and that is a latent no-op-but-wrong allocation otherwise).

Suggested fix

Guard against the negative return before allocating (both calloc sites):

xlen = GETXATTR(path, p, NULL, 0);
if (xlen < 0) continue;            /* cannot read this attr's value; skip it */
if (xlen == 0) continue;           /* nothing to compare */
if ((xvalue = (char*)calloc(1, (size_t)xlen)) == NULL)
    { stat = NC_ENOMEM; goto done; }

and likewise treat a LISTXATTR result of 0 (no attributes) as "not a DAOS container" without allocating.


Written by enterprise github copilot AI

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