From a6f11b5f0d8be7a6597b42e19686dce38c4a21a3 Mon Sep 17 00:00:00 2001 From: hja Date: Mon, 2 Nov 2026 20:12:17 +0000 Subject: [PATCH 1/2] Reject NULL contexts at liblsof API entry points --- .gitignore | 1 + Makefile.am | 8 +++++--- include/lsof.h | 2 +- lib/lsof.c | 15 +++++++++++---- tests/LTnull.c | 38 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 tests/LTnull.c diff --git a/.gitignore b/.gitignore index 49132e68..49c02eab 100644 --- a/.gitignore +++ b/.gitignore @@ -113,6 +113,7 @@ lib/dialects/netbsd/include # LTbasic LTbasic2 +LTnull LTbigf LTdnlc LTlock diff --git a/Makefile.am b/Makefile.am index c97cbdd3..283fca6f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -212,12 +212,14 @@ tests_LTszoff_CFLAGS = @LSOF_TEST_CFLAGS@ -I$(top_srcdir)/include tests_LTunix_SOURCES = tests/LTunix.c tests/LTlib.c tests_LTunix_CFLAGS = @LSOF_TEST_CFLAGS@ -I$(top_srcdir)/include -# LTbasic2 links against liblsof, only build when liblsof is enabled +# Library tests, only build when liblsof is enabled if INSTALL_LIBLSOF -TESTS += tests/LTbasic2 -check_PROGRAMS += tests/LTbasic2 +TESTS += tests/LTbasic2 tests/LTnull +check_PROGRAMS += tests/LTbasic2 tests/LTnull tests_LTbasic2_CFLAGS = @LSOF_TEST_CFLAGS@ -I$(top_srcdir)/include tests_LTbasic2_LDADD = liblsof.la +tests_LTnull_CFLAGS = -I$(top_srcdir)/include +tests_LTnull_LDADD = liblsof.la endif # Documentation diff --git a/include/lsof.h b/include/lsof.h index 30ee16e6..eadc2b2e 100644 --- a/include/lsof.h +++ b/include/lsof.h @@ -560,7 +560,7 @@ enum lsof_error lsof_freeze(struct lsof_context *ctx); * * If the context is not frozen, lsof_freeze() will be called. * - * \return LSOF_INVALID_ARGUMENT if either pointer argument is NULL + * \return LSOF_ERROR_INVALID_ARGUMENT if either pointer argument is NULL * * \since API version 1 */ diff --git a/lib/lsof.c b/lib/lsof.c index 94308d0e..46a30500 100644 --- a/lib/lsof.c +++ b/lib/lsof.c @@ -228,7 +228,7 @@ enum lsof_error lsof_set_output_stream(struct lsof_context *ctx, FILE *fp, API_EXPORT enum lsof_error lsof_freeze(struct lsof_context *ctx) { - if (ctx->frozen) { + if (!ctx || ctx->frozen) { return LSOF_ERROR_INVALID_ARGUMENT; } @@ -283,7 +283,7 @@ enum lsof_error lsof_gather(struct lsof_context *ctx, struct lsof_process *user_procs = NULL; struct lsof_result *res = NULL; - if (!result) { + if (!ctx || !result) { ret = LSOF_ERROR_INVALID_ARGUMENT; return ret; } else if (!ctx->frozen) { @@ -1192,8 +1192,12 @@ enum lsof_error lsof_select_pid_pgid(struct lsof_context *ctx, int32_t id, API_EXPORT enum lsof_error lsof_select_pid(struct lsof_context *ctx, uint32_t pid, int exclude) { - enum lsof_error res = lsof_select_pid_pgid(ctx, pid, &Spid, &Mxpid, &Npid, - &Npidi, &Npidx, exclude, 1); + enum lsof_error res; + if (!ctx) { + return LSOF_ERROR_INVALID_ARGUMENT; + } + res = lsof_select_pid_pgid(ctx, pid, &Spid, &Mxpid, &Npid, &Npidi, &Npidx, + exclude, 1); /* Record number of unselected PIDs for optimization */ Npuns = Npid; return res; @@ -1202,6 +1206,9 @@ enum lsof_error lsof_select_pid(struct lsof_context *ctx, uint32_t pid, API_EXPORT enum lsof_error lsof_select_pgid(struct lsof_context *ctx, uint32_t pgid, int exclude) { + if (!ctx) { + return LSOF_ERROR_INVALID_ARGUMENT; + } return lsof_select_pid_pgid(ctx, pgid, &Spgid, &Mxpgid, &Npgid, &Npgidi, &Npgidx, exclude, 0); } diff --git a/tests/LTnull.c b/tests/LTnull.c new file mode 100644 index 00000000..0bda0005 --- /dev/null +++ b/tests/LTnull.c @@ -0,0 +1,38 @@ +/* LTnull.c -- public liblsof entry points must reject NULL arguments. */ + +#include "lsof.h" +#include + +static int expect_invalid(const char *name, enum lsof_error result) { + if (result == LSOF_ERROR_INVALID_ARGUMENT) + return 0; + fprintf(stderr, "%s: expected invalid argument, got %d\n", name, result); + return 1; +} + +int main(void) { + struct lsof_context *ctx; + struct lsof_result *result = NULL; + int failed = 0; + + failed |= expect_invalid("gather(NULL, &result)", lsof_gather(NULL, &result)); + failed |= expect_invalid("gather(NULL, NULL)", lsof_gather(NULL, NULL)); + failed |= expect_invalid("freeze(NULL)", lsof_freeze(NULL)); + failed |= expect_invalid("select_pid(NULL)", lsof_select_pid(NULL, 1, 0)); + failed |= expect_invalid("select_pgid(NULL)", lsof_select_pgid(NULL, 1, 0)); + + ctx = lsof_new(); + if (!ctx) { + fprintf(stderr, "lsof_new failed\n"); + return 1; + } + failed |= expect_invalid("gather(ctx, NULL)", lsof_gather(ctx, NULL)); + /* Rejected arguments must not freeze or otherwise invalidate the context. */ + if (lsof_select_pid(ctx, 1, 0) != LSOF_SUCCESS || + lsof_select_pgid(ctx, 1, 0) != LSOF_SUCCESS) { + fprintf(stderr, "context is unusable after rejected gather\n"); + failed = 1; + } + lsof_destroy(ctx); + return failed; +} From d47d9d4e2a14658566d37bf35480a933c070231d Mon Sep 17 00:00:00 2001 From: hja Date: Tue, 3 Nov 2026 07:28:38 +0000 Subject: [PATCH 2/2] Document invalid-argument returns across the public API --- include/lsof.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/include/lsof.h b/include/lsof.h index eadc2b2e..5e6d45f0 100644 --- a/include/lsof.h +++ b/include/lsof.h @@ -427,6 +427,8 @@ struct lsof_context *lsof_new(); * By default, the output is suppressed. You can set fp to NULL to suppress * output. * + * \return LSOF_ERROR_INVALID_ARGUMENT if ctx is NULL + * * \since API version 1 */ enum lsof_error lsof_set_output_stream(struct lsof_context *ctx, FILE *fp, @@ -437,6 +439,8 @@ enum lsof_error lsof_set_output_stream(struct lsof_context *ctx, FILE *fp, * lsof may block when calling lstat(), readlink() or stat(). Call this function * with `avoid=1` to let lsof avoid calling these functions. * + * \return LSOF_ERROR_INVALID_ARGUMENT if ctx is NULL or frozen + * * \since API version 1 */ enum lsof_error lsof_avoid_blocking(struct lsof_context *ctx, int avoid); @@ -446,6 +450,8 @@ enum lsof_error lsof_avoid_blocking(struct lsof_context *ctx, int avoid); * To avoid being blocked by some kernel operations, liblsof does them in forked * child processes. Call this function to change this behavior. * + * \return LSOF_ERROR_INVALID_ARGUMENT if ctx is NULL or frozen + * * \since API version 1 */ enum lsof_error lsof_avoid_forking(struct lsof_context *ctx, int avoid); @@ -457,6 +463,8 @@ enum lsof_error lsof_avoid_forking(struct lsof_context *ctx, int avoid); * sockets OR open files by the user. If lsof_logic_and() is called, it will * report unix sockets open by the specified user. * + * \return LSOF_ERROR_INVALID_ARGUMENT if ctx is NULL or frozen + * * \since API version 1 */ enum lsof_error lsof_logic_and(struct lsof_context *ctx); @@ -468,6 +476,9 @@ enum lsof_error lsof_logic_and(struct lsof_context *ctx); * * You can call this function multiple times to add more search conditions. * + * \return LSOF_ERROR_INVALID_ARGUMENT if ctx is NULL or frozen, command is + * both included and excluded, or command exceeds the system's supported length + * * \since API version 1 */ enum lsof_error lsof_select_process(struct lsof_context *ctx, char *command, @@ -488,6 +499,9 @@ enum lsof_error lsof_select_process(struct lsof_context *ctx, char *command, * * You can call this function multiple times to add more search conditions. * + * \return LSOF_ERROR_INVALID_ARGUMENT if ctx is NULL or frozen, or the regular + * expression or its modifiers are invalid + * * \since API version 1 */ enum lsof_error lsof_select_process_regex(struct lsof_context *ctx, @@ -500,6 +514,9 @@ enum lsof_error lsof_select_process_regex(struct lsof_context *ctx, * * You can call this function multiple times to add more search conditions. * + * \return LSOF_ERROR_INVALID_ARGUMENT if ctx is NULL or frozen, or pid is + * both included and excluded + * * \since API version 1 */ enum lsof_error lsof_select_pid(struct lsof_context *ctx, uint32_t pid, @@ -512,6 +529,9 @@ enum lsof_error lsof_select_pid(struct lsof_context *ctx, uint32_t pid, * * You can call this function multiple times to add more search conditions. * + * \return LSOF_ERROR_INVALID_ARGUMENT if ctx is NULL or frozen, or pgid is + * both included and excluded + * * \since API version 1 */ enum lsof_error lsof_select_pgid(struct lsof_context *ctx, uint32_t pgid, @@ -523,6 +543,9 @@ enum lsof_error lsof_select_pgid(struct lsof_context *ctx, uint32_t pgid, * * You can call this function multiple times to add more search conditions. * + * \return LSOF_ERROR_INVALID_ARGUMENT if ctx is NULL or frozen, or uid is + * both included and excluded + * * \since API version 1 */ enum lsof_error lsof_select_uid(struct lsof_context *ctx, uint32_t uid, @@ -534,6 +557,9 @@ enum lsof_error lsof_select_uid(struct lsof_context *ctx, uint32_t uid, * * You can call this function multiple times to add more search conditions. * + * \return LSOF_ERROR_INVALID_ARGUMENT if ctx is NULL or frozen, login cannot + * be resolved to a UID, or that UID is both included and excluded + * * \since API version 1 */ enum lsof_error lsof_select_login(struct lsof_context *ctx, char *login, @@ -546,6 +572,8 @@ enum lsof_error lsof_select_login(struct lsof_context *ctx, char *login, * * The function allows liblsof to do some preprocessing to improve performance. * + * \return LSOF_ERROR_INVALID_ARGUMENT if ctx is NULL or already frozen + * * \since API version 1 */ enum lsof_error lsof_freeze(struct lsof_context *ctx);