Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## 2026-06-05 - [Fix buffer overflows by replacing strcpy and strcat]
**Vulnerability:** Unsafe buffer copy functions like strcpy and strcat were used.
**Learning:** Replaced strcpy and strcat with safer variants xsnprintf and xasprintf respectively to guarantee buffer bounds check and prevent buffer overflows.
**Prevention:** Always use safe bounds-checked buffer copy variants.
20 changes: 0 additions & 20 deletions ed.init.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,26 +272,6 @@ ed_Init(void)
{
ResetInLine(1); /* reset the input pointers */
GettingInput = 0; /* just in case */
#ifdef notdef
/* XXX This code was here before the kill ring:
LastKill = KillBuf; / * no kill buffer * /
If there was any reason for that other than to make sure LastKill
was initialized, the code below should go in here instead - but
it doesn't seem reasonable to lose the entire kill ring (which is
"self-initializing") just because you set $term or whatever, so
presumably this whole '#ifdef notdef' should just be taken out. */

{ /* no kill ring - why? */
int i;
for (i = 0; i < KillRingMax; i++) {
xfree(KillRing[i].buf);
KillRing[i].buf = NULL;
KillRing[i].len = 0;
}
YankPos = KillPos = 0;
KillRingLen = 0;
}
#endif

#ifdef DEBUG_EDIT
CheckMaps(); /* do a little error checking on key maps */
Expand Down
13 changes: 11 additions & 2 deletions glob.c
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,10 @@ glob3(struct strbuf *pathbuf, const Char *pattern, const Char *restpattern,
}

/* search directory for matching names */
while ((dp = readdir(dirp)) != NULL) {
for (;;) {
errno = 0;
if ((dp = readdir(dirp)) == NULL)
break;
/* initial DOT must be matched literally */
if (dp->d_name[0] == DOT && *pattern != DOT)
if (!(pglob->gl_flags & GLOB_DOT) || !dp->d_name[1] ||
Expand Down Expand Up @@ -647,7 +650,13 @@ glob3(struct strbuf *pathbuf, const Char *pattern, const Char *restpattern,
break;
}
}
/* todo: check error from readdir? */
if (dp == NULL && errno != 0) {
pathbuf->len = orig_len;
strbuf_terminate(pathbuf);
if ((pglob->gl_errfunc && (*pglob->gl_errfunc) (pathbuf->s, errno)) ||
(pglob->gl_flags & GLOB_ERR))
err = GLOB_ABEND;
}
closedir(dirp);
return (err);
}
Expand Down
12 changes: 4 additions & 8 deletions host.defs
Original file line number Diff line number Diff line change
Expand Up @@ -134,29 +134,25 @@ getconvex(void)

#ifdef SI_CPUTYPE_C2MP
case SI_CPUTYPE_C2MP:
(void) strcpy(result, "c2X0");
result[2] = sysinfo.cpu_count + '0';
(void) xsnprintf(result, sizeof(result), "c2%c0", sysinfo.cpu_count + '0');
return result;
#endif

#ifdef SI_CPUTYPE_C34
case SI_CPUTYPE_C34:
(void) strcpy(result, "c34X0");
result[3] = sysinfo.cpu_count + '0';
(void) xsnprintf(result, sizeof(result), "c34%c0", sysinfo.cpu_count + '0');
return result;
#endif

#ifdef SI_CPUTYPE_C38
case SI_CPUTYPE_C38:
(void) strcpy(result, "c38X0");
result[3] = sysinfo.cpu_count + '0';
(void) xsnprintf(result, sizeof(result), "c38%c0", sysinfo.cpu_count + '0');
return result;
#endif

#ifdef SI_CPUTYPE_C46
case SI_CPUTYPE_C46:
(void) strcpy(result, "c46X0");
result[3] = sysinfo.cpu_count + '0';
(void) xsnprintf(result, sizeof(result), "c46%c0", sysinfo.cpu_count + '0');
return result;
#endif

Expand Down
4 changes: 0 additions & 4 deletions sh.dol.c
Original file line number Diff line number Diff line change
Expand Up @@ -755,10 +755,6 @@ Dgetdol(void)

for (i = lwb - 1, length = 0; i < upb; i++)
length += Strlen(vp->vec[i]);
#ifdef notdef
/* We don't want that, since we can always compute it by adding $#xxx */
length += i - 1; /* Add the number of spaces in */
#endif
addla(putn((tcsh_number_t)length));
}
else {
Expand Down
83 changes: 46 additions & 37 deletions sh.parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ static struct command *syn1a (const struct wordent *, const struct wordent *, i
static struct command *syn1b (const struct wordent *, const struct wordent *, int);
static struct command *syn2 (const struct wordent *, const struct wordent *, int);
static struct command *syn3 (const struct wordent *, const struct wordent *, int);
static int syn3_is_specp (const struct wordent *, const struct wordent *);
static int syn3_count_args (const struct wordent *, const struct wordent *, int);

#define ALEFT 51 /* max of 50 alias expansions */
#define HLEFT 11 /* max of 10 history expansions */
Expand Down Expand Up @@ -467,36 +469,16 @@ syn2(const struct wordent *p1, const struct wordent *p2, int flags)

static const char RELPAR[] = {'<', '>', '(', ')', '\0'};

/*
* syn3
* ( syn0 ) [ < in ] [ > out ]
* word word* [ < in ] [ > out ]
* KEYWORD ( word* ) word* [ < in ] [ > out ]
*
* KEYWORD = (@ exit foreach if set switch test while)
*/
static struct command *
syn3(const struct wordent *p1, const struct wordent *p2, int flags)
static int
syn3_is_specp(const struct wordent *p1, const struct wordent *p2)
{
const struct wordent *p;
const struct wordent *lp, *rp;
struct command *t;
int l;
Char **av;
int n, c;
int specp = 0;
const struct wordent *p = p1;

if (p1 != p2) {
p = p1;
again:
while (p != p2) {
switch (srchx(p->word)) {

case TC_ELSE:
p = p->next;
if (p != p2)
goto again;
break;

continue;
case TC_EXIT:
case TC_FOREACH:
case TC_IF:
Expand All @@ -505,29 +487,33 @@ syn3(const struct wordent *p1, const struct wordent *p2, int flags)
case TC_SWITCH:
case TC_WHILE:
case TC_TEST:
specp = 1;
break;
return 1;
default:
break;
return 0;
}
}
n = 0;
l = 0;
for (p = p1; p != p2; p = p->next)
switch (p->word[0]) {
return 0;
}

static int
syn3_count_args(const struct wordent *p1, const struct wordent *p2, int specp)
{
const struct wordent *p;
int n = 0;
int l = 0;

for (p = p1; p != p2; p = p->next) {
switch (p->word[0]) {
case '(':
if (specp)
n++;
l++;
continue;

case ')':
if (specp)
n++;
l--;
continue;

case '>':
case '<':
if (l != 0) {
Expand All @@ -541,15 +527,38 @@ syn3(const struct wordent *p1, const struct wordent *p2, int flags)
continue;
n--;
continue;

default:
if (!specp && l != 0)
continue;
n++;
continue;
}
if (n < 0)
n = 0;
}
return (n < 0) ? 0 : n;
}

/*
* syn3
* ( syn0 ) [ < in ] [ > out ]
* word word* [ < in ] [ > out ]
* KEYWORD ( word* ) word* [ < in ] [ > out ]
*
* KEYWORD = (@ exit foreach if set switch test while)
*/
static struct command *
syn3(const struct wordent *p1, const struct wordent *p2, int flags)
{
const struct wordent *p;
const struct wordent *lp, *rp;
struct command *t;
int l;
Char **av;
int n, c;
int specp = 0;

specp = syn3_is_specp(p1, p2);
n = syn3_count_args(p1, p2, specp);

t = xcalloc(1, sizeof(*t));
av = xcalloc(n + 1, sizeof(Char **));
t->t_dcom = av;
Expand Down
2 changes: 2 additions & 0 deletions tc.alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,8 @@ showall(Char **v, struct command *c)
#endif /* SYSMALLOC */
USE(c);
USE(v);
USE(memtop);
USE(membot);
}

#ifndef SYSMALLOC
Expand Down
9 changes: 3 additions & 6 deletions tc.os.c
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,7 @@ dosetpath(Char **arglist, struct command *c)
if (val == NULL)
val = "";

spaths[i] = xmalloc((Strlen(pathvars[i]) + strlen(val) + 2) *
sizeof **spaths);
(void) strcpy(spaths[i], short2str(pathvars[i]));
(void) strcat(spaths[i], "=");
(void) strcat(spaths[i], val);
spaths[i] = xasprintf("%s=%s", short2str(pathvars[i]), val);
cpaths[i] = spaths[i];
}

Expand Down Expand Up @@ -783,7 +779,8 @@ dobs2cmd(Char **v, struct command *c)
len += Strlen(v[i]) + (v[i+1] != NULL);
}

cmd = xmalloc(len+1); /* 1 for the final '\0' *//* FIXME: memory leak? */
cmd = xmalloc(len+1); /* 1 for the final '\0' */
cleanup_push(cmd, xfree);

/* 2nd round: fill cmd buffer */
i = 0;
Expand Down
51 changes: 25 additions & 26 deletions tc.prompt.c
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,22 @@ git_get_info(const char *dir, char *branch, size_t branchsz,
while (llen > 0 && (target[llen-1] == '\n' || target[llen-1] == '\r'))
target[--llen] = '\0';
if (target[0] == '/') {
snprintf(resolved, sizeof(resolved), "%s", target);
if ((size_t)xsnprintf(resolved, sizeof(resolved), "%s", target) >= sizeof(resolved)) {
fclose(gf);
return 0;
}
} else {
snprintf(resolved, sizeof(resolved), "%s/%s", gitdir, target);
if ((size_t)xsnprintf(resolved, sizeof(resolved), "%s/%s", gitdir, target) >= sizeof(resolved)) {
fclose(gf);
return 0;
}
}
fclose(gf);
snprintf(gitdir, sizeof(gitdir), "%s", resolved);
if ((size_t)xsnprintf(gitdir, sizeof(gitdir), "%s", resolved) >= sizeof(gitdir)) {
fclose(gf);
return 0;
}
found = 1;
fclose(gf);
/* gitdir already points at the real git dir */
goto git_found;
}
Expand Down Expand Up @@ -308,15 +317,12 @@ git_get_info(const char *dir, char *branch, size_t branchsz,
if (len > 0 && path[len - 1] == '\n')
path[--len] = '\0';
if (strncmp(path, "ref: refs/heads/", 16) == 0) {
strncpy(branch, path + 16, branchsz - 1);
branch[branchsz - 1] = '\0';
xsnprintf(branch, branchsz, "%s", path + 16);
} else if (strncmp(path, "ref: ", 5) == 0) {
strncpy(branch, path + 5, branchsz - 1);
branch[branchsz - 1] = '\0';
xsnprintf(branch, branchsz, "%s", path + 5);
} else if (len >= 7) {
/* Detached HEAD: show first 7 hex chars */
strncpy(branch, path, 7);
branch[7] = '\0';
xsnprintf(branch, branchsz, "%.7s", path);
}
}
fclose(fp);
Expand All @@ -331,8 +337,7 @@ git_get_info(const char *dir, char *branch, size_t branchsz,
/* MERGE */
snprintf(probe, sizeof(probe), "%s/MERGE_HEAD", gitdir);
if (access(probe, F_OK) == 0) {
strncpy(op, "MERGING", opsz - 1);
op[opsz - 1] = '\0';
xsnprintf(op, opsz, "MERGING");
return 1;
}
/* REBASE (interactive) */
Expand All @@ -347,47 +352,41 @@ git_get_info(const char *dir, char *branch, size_t branchsz,
size_t rlen = strlen(rbranch);
if (rlen && rbranch[rlen-1] == '\n') rbranch[--rlen] = '\0';
if (strncmp(rbranch, "refs/heads/", 11) == 0)
strncpy(branch, rbranch + 11, branchsz - 1);
xsnprintf(branch, branchsz, "%s", rbranch + 11);
else
strncpy(branch, rbranch, branchsz - 1);
branch[branchsz - 1] = '\0';
xsnprintf(branch, branchsz, "%s", rbranch);
}
fclose(rf);
}
strncpy(op, "REBASING-i", opsz - 1);
op[opsz - 1] = '\0';
xsnprintf(op, opsz, "REBASING-i");
return 1;
}
/* REBASE (am/apply) */
snprintf(probe, sizeof(probe), "%s/rebase-apply", gitdir);
if (access(probe, F_OK) == 0) {
snprintf(probe, sizeof(probe), "%s/rebase-apply/rebasing", gitdir);
if (access(probe, F_OK) == 0)
strncpy(op, "REBASING", opsz - 1);
xsnprintf(op, opsz, "REBASING");
else
strncpy(op, "AM", opsz - 1);
op[opsz - 1] = '\0';
xsnprintf(op, opsz, "AM");
return 1;
}
/* CHERRY-PICK */
snprintf(probe, sizeof(probe), "%s/CHERRY_PICK_HEAD", gitdir);
if (access(probe, F_OK) == 0) {
strncpy(op, "CHERRY-PICKING", opsz - 1);
op[opsz - 1] = '\0';
xsnprintf(op, opsz, "CHERRY-PICKING");
return 1;
}
/* REVERT */
snprintf(probe, sizeof(probe), "%s/REVERT_HEAD", gitdir);
if (access(probe, F_OK) == 0) {
strncpy(op, "REVERTING", opsz - 1);
op[opsz - 1] = '\0';
xsnprintf(op, opsz, "REVERTING");
return 1;
}
/* BISECT */
snprintf(probe, sizeof(probe), "%s/BISECT_LOG", gitdir);
if (access(probe, F_OK) == 0) {
strncpy(op, "BISECTING", opsz - 1);
op[opsz - 1] = '\0';
xsnprintf(op, opsz, "BISECTING");
return 1;
Comment on lines +355 to 390

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The indentation of the newly added xsnprintf calls in this block is inconsistent. Please format them using tabs and spaces to match the surrounding code style.

			xsnprintf(branch, branchsz, "%s", rbranch + 11);
		    else
			xsnprintf(branch, branchsz, "%s", rbranch);
		}
		fclose(rf);
	    }
	    xsnprintf(op, opsz, "REBASING-i");
	    return 1;
	}
	/* REBASE (am/apply) */
	snprintf(probe, sizeof(probe), "%s/rebase-apply", gitdir);
	if (access(probe, F_OK) == 0) {
	    snprintf(probe, sizeof(probe), "%s/rebase-apply/rebasing", gitdir);
	    if (access(probe, F_OK) == 0)
		xsnprintf(op, opsz, "REBASING");
	    else
		xsnprintf(op, opsz, "AM");
	    return 1;
	}
	/* CHERRY-PICK */
	snprintf(probe, sizeof(probe), "%s/CHERRY_PICK_HEAD", gitdir);
	if (access(probe, F_OK) == 0) {
	    xsnprintf(op, opsz, "CHERRY-PICKING");
	    return 1;
	}
	/* REVERT */
	snprintf(probe, sizeof(probe), "%s/REVERT_HEAD", gitdir);
	if (access(probe, F_OK) == 0) {
	    xsnprintf(op, opsz, "REVERTING");
	    return 1;
	}
	/* BISECT */
	snprintf(probe, sizeof(probe), "%s/BISECT_LOG", gitdir);
	if (access(probe, F_OK) == 0) {
	    xsnprintf(op, opsz, "BISECTING");
	    return 1;
	}

}
}
Expand Down
Loading
Loading