Skip to content

Commit 1b657bc

Browse files
committed
msearch: harden argument parsing and sqlite row handling
Validate -l limit parsing, treat library failures as errors, and add NULL checks for sqlite3_column_text() results. Fix owner lookup to use the row uid. Add basic ATF tests for argument handling. AI-Assisted-by: GPT-5.2 (Codex CLI) Signed-off-by: Lucas Holt <luke@foolishgames.com>
1 parent fb1907b commit 1b657bc

6 files changed

Lines changed: 80 additions & 8 deletions

File tree

lib/libmsearch/msearch_fulltext.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ msearch_fulltext_search(msearch_query *query, msearch_result *result) {
7979
while (1) {
8080
ret = sqlite3_step(stmt);
8181
if (ret == SQLITE_ROW) {
82+
const unsigned char *path;
83+
84+
path = sqlite3_column_text(stmt, 0);
85+
if (path == NULL) {
86+
i = -1;
87+
break;
88+
}
8289
if (i > 0) {
8390
current->next = malloc(sizeof(msearch_result));
8491
if (current->next == NULL) {
@@ -87,12 +94,12 @@ msearch_fulltext_search(msearch_query *query, msearch_result *result) {
8794
}
8895
current = current->next;
8996
}
90-
current->path = strdup(sqlite3_column_text(stmt, 0));
97+
current->path = strdup(path);
9198
current->weight = sqlite3_column_double(stmt, 1);
9299
current->filename = NULL;
93-
if (lstat(sqlite3_column_text(stmt, 0), &sb) == 0) {
100+
if (lstat(path, &sb) == 0) {
94101
if (S_ISREG(sb.st_mode)) {
95-
current->filename = strdup(basename((char *) sqlite3_column_text(stmt, 0)));
102+
current->filename = strdup(basename((char *)path));
96103
}
97104
current->size = sb.st_size;
98105
current->uid = sb.st_uid;

lib/libmsearch/msearch_search.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,13 @@ msearch_search(msearch_query *query, msearch_result *result) {
9191
while (1) {
9292
ret = sqlite3_step(stmt);
9393
if (ret == SQLITE_ROW) {
94+
const unsigned char *path;
95+
96+
path = sqlite3_column_text(stmt, 0);
97+
if (path == NULL) {
98+
i = -1;
99+
break;
100+
}
94101
if (i > 0) {
95102
current->next = malloc(sizeof(msearch_result));
96103
if (current->next == NULL) {
@@ -100,14 +107,15 @@ msearch_search(msearch_query *query, msearch_result *result) {
100107
current = current->next;
101108
}
102109
current->filename = NULL;
103-
if (lstat(sqlite3_column_text(stmt, 0), &sb) == 0) {
110+
if (lstat(path, &sb) == 0) {
104111
if (S_ISREG(sb.st_mode)) {
105-
current->filename = strdup(basename((char *)sqlite3_column_text(stmt, 0)));
112+
current->filename = strdup(basename((char *)path));
106113
}
107114
}
108-
current->path = strdup(sqlite3_column_text(stmt, 0));
115+
current->path = strdup(path);
109116
current->size = sqlite3_column_int64(stmt, 1);
110117
current->uid = sqlite3_column_int(stmt, 2);
118+
uid = current->uid;
111119
if ((getpwuid_r(uid, &pwd, buf, sizeof(buf), &pwdbuf)) == 0 && pwdbuf != NULL) {
112120
current->owner = strdup(pwdbuf->pw_name);
113121
} else

usr.bin/msearch/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.include <src.opts.mk>
2+
13
PROG= msearch
24

35
CFLAGS+= -I${.CURDIR}/../../lib/libsearch
@@ -6,4 +8,9 @@ CSTD= c99
68

79
LIBADD+= msearch sqlite3 z
810

11+
.if ${MK_TESTS} != "no"
12+
HAS_TESTS=
13+
SUBDIR+= tests
14+
.endif
15+
916
.include <bsd.prog.mk>

usr.bin/msearch/msearch.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
#include <stdlib.h>
3131
#include <string.h>
3232
#include <err.h>
33+
#include <errno.h>
34+
#include <limits.h>
3335
#include <unistd.h>
3436
#include <msearch.h>
3537

@@ -55,8 +57,18 @@ main(int argc, char *argv[]) {
5557
cflag = 1;
5658
break;
5759
case 'l':
58-
limit = (int)strtol(optarg, (char **)NULL, 10);
60+
{
61+
char *endp;
62+
long llimit;
63+
64+
errno = 0;
65+
llimit = strtol(optarg, &endp, 10);
66+
if (errno != 0 || endp == optarg || *endp != '\0' ||
67+
llimit < 0 || llimit > INT_MAX)
68+
errx(1, "invalid limit: %s", optarg);
69+
limit = (int)llimit;
5970
break;
71+
}
6072
case 'r':
6173
rflag = 1;
6274
break;
@@ -79,7 +91,7 @@ main(int argc, char *argv[]) {
7991
err(1, NULL);
8092
}
8193

82-
if ((query = malloc(sizeof(msearch_query))) == NULL) {
94+
if ((query = calloc(1, sizeof(*query))) == NULL) {
8395
err(1, NULL);
8496
}
8597

@@ -92,6 +104,8 @@ main(int argc, char *argv[]) {
92104
query->limit = limit;
93105

94106
results = msearch(query, result);
107+
if (results < 0)
108+
errx(1, "msearch failed");
95109

96110
if (cflag) {
97111
printf("%d\n", results);

usr.bin/msearch/tests/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
ATF_TESTS_SH= msearch_test
3+
4+
.include <bsd.test.mk>
5+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#
2+
# Basic regression tests for msearch(1) argument handling.
3+
#
4+
5+
atf_test_case usage_no_args
6+
usage_no_args_body()
7+
{
8+
MSEARCH_BIN="$(ls /usr/obj/usr/src/*/usr.bin/msearch/msearch 2>/dev/null | head -n 1)"
9+
if [ -z "${MSEARCH_BIN}" ] || [ ! -x "${MSEARCH_BIN}" ]; then
10+
MSEARCH_BIN="msearch"
11+
fi
12+
atf_check -s exit:1 -e match:"^usage: msearch" "${MSEARCH_BIN}"
13+
}
14+
15+
atf_test_case invalid_limit_rejected
16+
invalid_limit_rejected_body()
17+
{
18+
MSEARCH_BIN="$(ls /usr/obj/usr/src/*/usr.bin/msearch/msearch 2>/dev/null | head -n 1)"
19+
if [ -z "${MSEARCH_BIN}" ] || [ ! -x "${MSEARCH_BIN}" ]; then
20+
MSEARCH_BIN="msearch"
21+
fi
22+
atf_check -s exit:1 -e match:"invalid limit" "${MSEARCH_BIN}" -l notanint foo
23+
atf_check -s exit:1 -e match:"invalid limit" "${MSEARCH_BIN}" -l -1 foo
24+
atf_check -s exit:1 -e match:"invalid limit" "${MSEARCH_BIN}" -l 999999999999999999999 foo
25+
}
26+
27+
atf_init_test_cases()
28+
{
29+
atf_add_test_case usage_no_args
30+
atf_add_test_case invalid_limit_rejected
31+
}

0 commit comments

Comments
 (0)