Fix ASAN/LSAN failures: ppath_home stack-use-after-scope + intentional leak
pclsync/ppath.c: Move buff[4096] to function scope in ppath_home() so the pointer stored in dir via result->pw_dir remains live through the putil_strdup(dir) call. Previously buff went out of scope at the if-block close, causing a stack-use-after-scope ASAN report on every call that fell through the getpwuid_r path. tests/unit-tests/test_ptools_errptr.c: run_unfixed() intentionally leaks errPtr to demonstrate the pre-fix bug. Wrap the allocation with LSAN_DISABLE() / LSAN_ENABLE() so LSAN does not abort the process at exit before stdio flushes, which was causing a non-zero exit code. The guard uses nested #ifdef/__has_feature to remain compatible with both GCC (__SANITIZE_ADDRESS__) and Clang (__has_feature(address_sanitizer)) without triggering "missing binary operator" errors on GCC. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
92bfab2ba4
commit
b30efe68fb
|
|
@ -58,12 +58,14 @@ int64_t ppath_free_space(const char *path) {
|
|||
char *ppath_home() {
|
||||
struct stat st;
|
||||
const char *dir;
|
||||
/* buff must be function-scoped: dir may point into it (via pw_dir) and
|
||||
* must remain valid through the putil_strdup(dir) call below. */
|
||||
char buff[4096];
|
||||
dir = getenv("HOME");
|
||||
if (pdbg_unlikely(!dir) || pdbg_unlikely(stat(dir, &st)) ||
|
||||
pdbg_unlikely(!pfile_stat_mode_ok(&st, 7))) {
|
||||
struct passwd pwd;
|
||||
struct passwd *result;
|
||||
char buff[4096];
|
||||
if (pdbg_unlikely(getpwuid_r(getuid(), &pwd, buff, sizeof(buff), &result)) ||
|
||||
pdbg_unlikely(stat(result->pw_dir, &st)) ||
|
||||
pdbg_unlikely(!pfile_stat_mode_ok(&st, 7)))
|
||||
|
|
|
|||
|
|
@ -18,6 +18,28 @@
|
|||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* Suppress LSAN reports for intentional leaks used to verify bug behaviour.
|
||||
* GCC sets __SANITIZE_ADDRESS__; Clang exposes __has_feature as a built-in.
|
||||
* Use nested #if so the __has_feature() call is only evaluated when the
|
||||
* compiler actually understands it (avoids "missing binary operator" on GCC). */
|
||||
#ifdef __SANITIZE_ADDRESS__
|
||||
# include <sanitizer/lsan_interface.h>
|
||||
# define LSAN_DISABLE() __lsan_disable()
|
||||
# define LSAN_ENABLE() __lsan_enable()
|
||||
#elif defined(__has_feature)
|
||||
# if __has_feature(address_sanitizer)
|
||||
# include <sanitizer/lsan_interface.h>
|
||||
# define LSAN_DISABLE() __lsan_disable()
|
||||
# define LSAN_ENABLE() __lsan_enable()
|
||||
# else
|
||||
# define LSAN_DISABLE() do {} while (0)
|
||||
# define LSAN_ENABLE() do {} while (0)
|
||||
# endif
|
||||
#else
|
||||
# define LSAN_DISABLE() do {} while (0)
|
||||
# define LSAN_ENABLE() do {} while (0)
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Allocation tracking via --wrap */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
@ -119,6 +141,9 @@ static int run_unfixed(void) {
|
|||
char *errPtr = NULL;
|
||||
int callRes;
|
||||
|
||||
/* Intentional leak: suppress LSAN so the process exits cleanly and
|
||||
* stdio flushes before the sanitizer reports it. */
|
||||
LSAN_DISABLE();
|
||||
callRes = mock_backend_call_1(&errPtr);
|
||||
(void)callRes;
|
||||
|
||||
|
|
@ -129,6 +154,7 @@ static int run_unfixed(void) {
|
|||
|
||||
if (errPtr)
|
||||
free(errPtr);
|
||||
LSAN_ENABLE();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue