Skip to content

Commit db938d6

Browse files
authored
Remove variable-length arrays (#246)
2 parents b941012 + a5750a0 commit db938d6

10 files changed

Lines changed: 94 additions & 76 deletions

File tree

arith.c

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Yash: yet another shell */
22
/* arith.c: arithmetic expansion */
3-
/* (C) 2007-2025 magicant */
3+
/* (C) 2007-2026 magicant */
44

55
/* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -287,10 +287,10 @@ bool do_assignment(const word_T *word, const value_T *value)
287287
if (vstr == NULL)
288288
return false;
289289

290-
wchar_t name[word->length + 1];
291-
wmemcpy(name, word->contents, word->length);
292-
name[word->length] = L'\0';
293-
return set_variable(name, vstr, SCOPE_GLOBAL, false);
290+
wchar_t *name = xwcsndup(word->contents, word->length);
291+
bool ok = set_variable(name, vstr, SCOPE_GLOBAL, false);
292+
free(name);
293+
return ok;
294294
}
295295

296296
/* Converts `value' to a newly-malloced wide string.
@@ -306,16 +306,21 @@ wchar_t *value_to_string(const value_T *value)
306306
return malloc_wprintf(L"%.*g", DBL_DIG, value->v_double);
307307
case VT_VAR:
308308
{
309-
wchar_t name[value->v_var.length + 1];
310-
wmemcpy(name, value->v_var.contents, value->v_var.length);
311-
name[value->v_var.length] = L'\0';
309+
wchar_t *name = xwcsndup(
310+
value->v_var.contents, value->v_var.length);
312311
const wchar_t *var = getvar(name);
313-
if (var != NULL)
314-
return xwcsdup(var);
315-
if (shopt_unset)
316-
return malloc_wprintf(L"%ld", 0L);
317-
xerror(0, Ngt("arithmetic: parameter `%ls' is not set"), name);
318-
return NULL;
312+
wchar_t *result;
313+
if (var != NULL) {
314+
result = xwcsdup(var);
315+
} else if (shopt_unset) {
316+
result = malloc_wprintf(L"%ld", 0L);
317+
} else {
318+
xerror(0, Ngt("arithmetic: parameter `%ls' is not set"),
319+
name);
320+
result = NULL;
321+
}
322+
free(name);
323+
return result;
319324
}
320325
}
321326
UNREACHABLE();
@@ -1097,14 +1102,13 @@ void parse_primary(evalinfo_T *info, value_T *result)
10971102
void parse_as_number(evalinfo_T *info, value_T *result)
10981103
{
10991104
word_T *word = &info->atoken.word;
1100-
wchar_t wordstr[word->length + 1];
1101-
wcsncpy(wordstr, word->contents, word->length);
1102-
wordstr[word->length] = L'\0';
1105+
wchar_t *wordstr = xwcsndup(word->contents, word->length);
11031106

11041107
long longresult;
11051108
if (xwcstol(wordstr, 0, &longresult)) {
11061109
result->type = VT_LONG;
11071110
result->v_long = longresult;
1111+
free(wordstr);
11081112
return;
11091113
}
11101114
if (!posixly_correct) {
@@ -1118,10 +1122,12 @@ void parse_as_number(evalinfo_T *info, value_T *result)
11181122
if (ok) {
11191123
result->type = VT_DOUBLE;
11201124
result->v_double = doubleresult;
1125+
free(wordstr);
11211126
return;
11221127
}
11231128
}
11241129
xerror(0, Ngt("arithmetic: `%ls' is not a valid number"), wordstr);
1130+
free(wordstr);
11251131
info->error = true;
11261132
result->type = VT_INVALID;
11271133
}
@@ -1137,17 +1143,17 @@ void coerce_number(evalinfo_T *info, value_T *value)
11371143
const wchar_t *varvalue;
11381144
{
11391145
word_T *name = &value->v_var;
1140-
wchar_t namestr[name->length + 1];
1141-
wmemcpy(namestr, name->contents, name->length);
1142-
namestr[name->length] = L'\0';
1146+
wchar_t *namestr = xwcsndup(name->contents, name->length);
11431147
varvalue = getvar(namestr);
11441148

11451149
if (varvalue == NULL && !shopt_unset) {
11461150
xerror(0, Ngt("arithmetic: parameter `%ls' is not set"), namestr);
1151+
free(namestr);
11471152
info->error = true;
11481153
value->type = VT_INVALID;
11491154
return;
11501155
}
1156+
free(namestr);
11511157
}
11521158
if (varvalue == NULL || varvalue[0] == L'\0') {
11531159
value->type = VT_LONG;

configure

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ confighdefs=''
358358
# define options for debugging
359359
if ${debug}
360360
then
361-
cflags="${CFLAGS--pedantic -MMD -Wall -Wextra -Og -fno-inline -ggdb}"
361+
cflags="${CFLAGS--pedantic -MMD -Wall -Wextra -Wvla -Og -fno-inline -ggdb}"
362362
else
363363
defconfigh "NDEBUG"
364364
fi

exec.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Yash: yet another shell */
22
/* exec.c: command execution */
3-
/* (C) 2007-2025 magicant */
3+
/* (C) 2007-2026 magicant */
44

55
/* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -1118,7 +1118,7 @@ wchar_t **invoke_simple_command(
11181118
void exec_external_program(
11191119
const char *path, int argc, char *argv0, void **argv, char **envs)
11201120
{
1121-
char *mbsargv[argc + 1];
1121+
char **mbsargv = xmalloce(argc, 1, sizeof *mbsargv);
11221122
mbsargv[0] = argv0;
11231123
for (int i = 1; i < argc; i++) {
11241124
mbsargv[i] = malloc_wcstombs(argv[i]);
@@ -1148,6 +1148,7 @@ void exec_external_program(
11481148

11491149
for (int i = 1; i < argc; i++)
11501150
free(mbsargv[i]);
1151+
free(mbsargv);
11511152
}
11521153

11531154
/* Calls `execve' until it doesn't return EINTR. */
@@ -1167,7 +1168,7 @@ void exec_fall_back_on_sh(
11671168
{
11681169
assert(argv[argc] == NULL);
11691170

1170-
char *args[argc + 3];
1171+
char **args = xmalloce(argc, 3, sizeof *args);
11711172
size_t index = 0;
11721173
args[index++] = "sh";
11731174
args[index++] = (char *) "-";
@@ -1195,6 +1196,7 @@ void exec_fall_back_on_sh(
11951196
xexecve(shpath, args, envp);
11961197
else
11971198
errno = ENOENT;
1199+
free(args);
11981200
xerror(errno, Ngt("cannot invoke a new shell to execute script `%s'"),
11991201
argv[0]);
12001202
}

lineedit/complete.c

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Yash: yet another shell */
22
/* complete.c: command line completion */
3-
/* (C) 2007-2025 magicant */
3+
/* (C) 2007-2026 magicant */
44

55
/* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -1157,10 +1157,9 @@ size_t get_common_prefix_length(void)
11571157
common_prefix_length = cpl;
11581158

11591159
if (le_state_is_compdebug) {
1160-
wchar_t value[common_prefix_length + 1];
1161-
wmemcpy(value, cand->origvalue, common_prefix_length);
1162-
value[common_prefix_length] = L'\0';
1160+
wchar_t *value = xwcsndup(cand->origvalue, common_prefix_length);
11631161
le_compdebug("candidate common prefix: \"%ls\"", value);
1162+
free(value);
11641163
}
11651164

11661165
return common_prefix_length;
@@ -1217,11 +1216,9 @@ void update_main_buffer(bool subst, bool finish)
12171216
assert(srclen <= cpl);
12181217
cand = le_candidates.contents[0];
12191218

1220-
size_t valuelen = cpl - srclen;
1221-
wchar_t value[valuelen + 1];
1222-
wcsncpy(value, cand->origvalue + srclen, valuelen);
1223-
value[valuelen] = L'\0';
1219+
wchar_t *value = xwcsndup(cand->origvalue + srclen, cpl - srclen);
12241220
quote(&quoted, value, quotetype);
1221+
free(value);
12251222
} else {
12261223
// Quote the selected candidate.
12271224
cand = le_candidates.contents[le_selected_candidate_index];

lineedit/editing.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Yash: yet another shell */
22
/* editing.c: main editing module */
3-
/* (C) 2007-2025 magicant */
3+
/* (C) 2007-2026 magicant */
44

55
/* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -3247,13 +3247,14 @@ void cmd_emacs_delete_horizontal_space(wchar_t c __attribute__((unused)))
32473247
* If the count is specified, blanks are replaced with `count' spaces. */
32483248
void cmd_emacs_just_one_space(wchar_t c __attribute__((unused)))
32493249
{
3250+
#define MAX_SPACE_COUNT 1000 /* The count is limited to avoid overflow. */
32503251
int count = get_count(1);
32513252
if (count < 0)
32523253
count = 0;
3253-
else if (count > 1000)
3254-
count = 1000;
3254+
else if (count > MAX_SPACE_COUNT)
3255+
count = MAX_SPACE_COUNT;
32553256

3256-
wchar_t s[count + 1];
3257+
wchar_t s[MAX_SPACE_COUNT + 1];
32573258
wmemset(s, L' ', count);
32583259
s[count] = L'\0';
32593260

parser.c

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Yash: yet another shell */
22
/* parser.c: syntax parser */
3-
/* (C) 2007-2025 magicant */
3+
/* (C) 2007-2026 magicant */
44

55
/* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -883,10 +883,9 @@ void print_errmsg_token(parsestate_T *ps, const char *message)
883883
assert(ps->index <= ps->next_index);
884884
assert(ps->next_index <= ps->src.length);
885885
size_t length = ps->next_index - ps->index;
886-
wchar_t token[length + 1];
887-
wcsncpy(token, &ps->src.contents[ps->index], length);
888-
token[length] = L'\0';
886+
wchar_t *token = xwcsndup(&ps->src.contents[ps->index], length);
889887
serror(ps, message, token);
888+
free(token);
890889
}
891890

892891
const char *get_errmsg_unexpected_tokentype(tokentype_T tokentype)
@@ -4087,13 +4086,8 @@ void print_embedded_command(struct print *pr, embedcmd_T ec, unsigned indent)
40874086
return;
40884087
}
40894088

4090-
size_t save_count = pr->pending_heredocs.length;
4091-
size_t extended_count = save_count;
4092-
if (extended_count == 0)
4093-
extended_count = 1; // A variable-length array must not be empty.
4094-
void *save_heredocs[extended_count];
4095-
memcpy(save_heredocs, pr->pending_heredocs.contents, sizeof save_heredocs);
4096-
pl_truncate(&pr->pending_heredocs, 0);
4089+
plist_T save_heredocs = pr->pending_heredocs;
4090+
pl_init(&pr->pending_heredocs);
40974091

40984092
print_and_or_lists(pr, ec.value.preparsed, indent, true);
40994093

@@ -4107,7 +4101,8 @@ void print_embedded_command(struct print *pr, embedcmd_T ec, unsigned indent)
41074101
}
41084102

41094103
assert(pr->pending_heredocs.length == 0);
4110-
pl_ncat(&pr->pending_heredocs, save_heredocs, save_count);
4104+
pl_destroy(&pr->pending_heredocs);
4105+
pr->pending_heredocs = save_heredocs;
41114106
}
41124107

41134108
void print_indent(struct print *pr, unsigned indent)

path.c

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Yash: yet another shell */
22
/* path.c: filename-related utilities */
3-
/* (C) 2007-2025 magicant */
3+
/* (C) 2007-2026 magicant */
44

55
/* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -160,13 +160,17 @@ bool check_access(const char *path, mode_t mode, int amode)
160160

161161
int gcount = getgroups(0, &gid); /* the second argument is a dummy */
162162
if (gcount > 0) {
163-
gid_t groups[gcount];
163+
gid_t *groups = xmallocn(gcount, sizeof *groups);
164164
gcount = getgroups(gcount, groups);
165165
if (gcount > 0) {
166-
for (int i = 0; i < gcount; i++)
167-
if (gid == groups[i])
166+
for (int i = 0; i < gcount; i++) {
167+
if (gid == groups[i]) {
168+
free(groups);
168169
return st.st_mode & S_IRWXG;
170+
}
171+
}
169172
}
173+
free(groups);
170174
}
171175

172176
return st.st_mode & S_IRWXO;
@@ -283,22 +287,25 @@ char *which(
283287
return NULL;
284288

285289
size_t namelen = strlen(name);
290+
xstrbuf_T path;
291+
sb_init(&path);
286292
for (const char *dir; (dir = *dirs) != NULL; dirs++) {
287293
size_t dirlen = strlen(dir);
288-
char path[dirlen + namelen + 3];
294+
sb_clear(&path);
289295
if (dirlen > 0) {
290296
/* concatenate `dir' and `name' to produce a pathname `path' */
291-
strcpy(path, dir);
292-
if (path[dirlen - 1] != '/')
293-
path[dirlen++] = '/';
294-
strcpy(path + dirlen, name);
297+
sb_ncat_force(&path, dir, dirlen);
298+
if (path.contents[dirlen - 1] != '/')
299+
sb_ccat(&path, '/');
300+
sb_ncat_force(&path, name, namelen);
295301
} else {
296302
/* if `dir' is empty, it's considered to be the current directory */
297-
strcpy(path, name);
303+
sb_ncat_force(&path, name, namelen);
298304
}
299-
if (cond(path))
300-
return xstrdup(path);
305+
if (cond(path.contents))
306+
return sb_tostr(&path);
301307
}
308+
sb_destroy(&path);
302309
return NULL;
303310
}
304311

@@ -717,16 +724,18 @@ plist_T wglob_parse_pattern(const wchar_t *pattern, enum wglobflags_T flags)
717724
plist_T components;
718725
pl_init(&components);
719726

727+
xwcsbuf_T component;
728+
wb_init(&component);
729+
720730
for (;;) {
721731
const wchar_t *slash = wcschr(pattern, L'/');
722732
size_t componentlength =
723733
(slash != NULL) ? (size_t) (slash - pattern) : wcslen(pattern);
724-
wchar_t component[componentlength + 1];
725-
wcsncpy(component, pattern, componentlength);
726-
component[componentlength] = L'\0';
734+
wb_clear(&component);
735+
wb_ncat(&component, pattern, componentlength);
727736

728-
struct wglob_pattern *c =
729-
wglob_parse_component(component, flags, slash != NULL);
737+
struct wglob_pattern *c = wglob_parse_component(
738+
component.contents, flags, slash != NULL);
730739
if (c == NULL) {
731740
pl_clear(&components, wglob_free_pattern_vp);
732741
break;
@@ -738,6 +747,7 @@ plist_T wglob_parse_pattern(const wchar_t *pattern, enum wglobflags_T flags)
738747
pattern = &slash[1];
739748
}
740749

750+
wb_destroy(&component);
741751
return components;
742752
}
743753

variable.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* Yash: yet another shell */
22
/* variable.c: deals with shell variables and parameters */
3-
/* (C) 2007-2025 magicant */
3+
/* (C) 2007-2026 magicant */
44

55
/* This program is free software: you can redistribute it and/or modify
66
* it under the terms of the GNU General Public License as published by
@@ -2161,10 +2161,7 @@ void array_remove_elements(
21612161
const wchar_t *name, variable_T *array, size_t count,
21622162
void *const *indexwcss)
21632163
{
2164-
size_t extended_count = count;
2165-
if (extended_count == 0)
2166-
extended_count = 1; // A variable-length array must not be empty.
2167-
long indices[extended_count];
2164+
long *indices = xmallocn(count, sizeof *indices);
21682165

21692166
assert((array->v_type & VF_MASK) == VF_ARRAY);
21702167

@@ -2173,6 +2170,7 @@ void array_remove_elements(
21732170
const wchar_t *indexwcs = indexwcss[i];
21742171
if (!xwcstol(indexwcs, 10, &indices[i])) {
21752172
xerror(errno, Ngt("`%ls' is not a valid integer"), indexwcs);
2173+
free(indices);
21762174
return;
21772175
}
21782176

@@ -2210,6 +2208,7 @@ void array_remove_elements(
22102208
}
22112209
array->v_valc = newcount;
22122210
array->v_vals[newcount] = NULL;
2211+
free(indices);
22132212

22142213
if (newcount < oldcount) {
22152214
variable_set(name, array);

0 commit comments

Comments
 (0)