Skip to content

Commit 46816e8

Browse files
added bounds checking to snprintf statements (#7122)
* added bounds checking to snprintf statements * nocov internal error routine --------- Co-authored-by: Michael Chirico <[email protected]>
1 parent c806849 commit 46816e8

File tree

9 files changed

+28
-27
lines changed

9 files changed

+28
-27
lines changed

src/assign.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -735,12 +735,11 @@ SEXP assign(SEXP dt, SEXP rows, SEXP cols, SEXP newcolnames, SEXP values)
735735
return(dt); // needed for `*tmp*` mechanism (when := isn't used), and to return the new object after a := for compound syntax.
736736
}
737737

738-
#define MSGSIZE 1000
739-
static char memrecycle_message[MSGSIZE+1]; // returned to rbindlist so it can prefix with which one of the list of data.table-like objects
738+
static char memrecycle_message[1000]; // returned to rbindlist so it can prefix with which one of the list of data.table-like objects
740739

741740
const char *columnDesc(int colnum, const char *colname) {
742-
static char column_desc[MSGSIZE+1]; // can contain column names, hence relatively large allocation.
743-
snprintf(column_desc, MSGSIZE, _("(column %d named '%s')"), colnum, colname);
741+
static char column_desc[sizeof(memrecycle_message)]; // can contain column names, hence relatively large allocation.
742+
snprintf(column_desc, sizeof(memrecycle_message), _("(column %d named '%s')"), colnum, colname);
744743
return column_desc;
745744
}
746745

@@ -941,7 +940,7 @@ const char *memrecycle(const SEXP target, const SEXP where, const int start, con
941940
if (COND) { \
942941
const char *sType = sourceIsI64 ? "integer64" : type2char(TYPEOF(source)); \
943942
const char *tType = targetIsI64 ? "integer64" : type2char(TYPEOF(target)); \
944-
snprintf(memrecycle_message, MSGSIZE, FMT, \
943+
snprintf(memrecycle_message, sizeof(memrecycle_message), FMT, \
945944
FMTVAL, sType, i+1, tType, \
946945
/* NB: important for () to be part of the translated string as a signal of nominative case to translators */ \
947946
colnum == 0 ? _("(target vector)") : columnDesc(colnum, colname)); \

src/fmelt.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ SEXP getvarcols(SEXP DT, SEXP dtnames, Rboolean varfactor, Rboolean verbose, str
612612
for (int j=0, ansloc=0, level=1; j<data->lmax; ++j) {
613613
const int thislen = data->narm ? length(VECTOR_ELT(data->not_NA_indices, j)) : data->nrow;
614614
char buff[20];
615-
snprintf(buff, 20, "%d", level++); // # notranslate
615+
snprintf(buff, sizeof(buff), "%d", level++); // # notranslate
616616
for (int k=0; k<thislen; ++k) SET_STRING_ELT(target, ansloc++, mkChar(buff));
617617
}
618618
}
@@ -649,7 +649,7 @@ SEXP getvarcols(SEXP DT, SEXP dtnames, Rboolean varfactor, Rboolean verbose, str
649649
for (int j=0, ansloc=0; j<data->lmax; ++j) {
650650
const int thislen = data->narm ? length(VECTOR_ELT(data->not_NA_indices, j)) : data->nrow;
651651
char buff[20];
652-
snprintf(buff, 20, "%d", nlevel+1); // # notranslate
652+
snprintf(buff, sizeof(buff), "%d", nlevel + 1); // # notranslate
653653
SET_STRING_ELT(levels, nlevel++, mkChar(buff)); // generate levels = 1:nlevels
654654
for (int k=0; k<thislen; ++k) td[ansloc++] = nlevel;
655655
}

src/forder.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,17 +99,19 @@ static void cleanup(void) {
9999
savetl_end(); // Restore R's own usage of tl. Must run after the for loop in free_ustr() since only CHARSXP which had tl>0 (R's usage) are stored there.
100100
}
101101

102+
// # nocov start
102103
void internal_error_with_cleanup(const char *call_name, const char *format, ...) {
103104
char buff[1024];
104105
va_list args;
105106
va_start(args, format);
106107

107-
vsnprintf(buff, 1023, format, args);
108+
vsnprintf(buff, sizeof(buff), format, args);
108109
va_end(args);
109110

110111
cleanup();
111112
error("%s %s: %s. %s", _("Internal error in"), call_name, buff, _("Please report to the data.table issues tracker."));
112113
}
114+
// # nocov end
113115

114116
static void push(const int *x, const int n) {
115117
if (!retgrp) return; // clearer to have the switch here rather than before each call

src/freadR.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -233,9 +233,9 @@ static void applyDrop(SEXP items, int8_t *type, int ncol, int dropSource) {
233233
for (int j=0; j<n; ++j) {
234234
int k = itemsD[j];
235235
if (k==NA_INTEGER || k<1 || k>ncol) {
236-
static char buff[51];
237-
if (dropSource==-1) snprintf(buff, 50, "drop[%d]", j+1); // # notranslate
238-
else snprintf(buff, 50, "colClasses[[%d]][%d]", dropSource+1, j+1); // # notranslate
236+
static char buff[50];
237+
if (dropSource==-1) snprintf(buff, sizeof(buff), "drop[%d]", j + 1); // # notranslate
238+
else snprintf(buff, sizeof(buff), "colClasses[[%d]][%d]", dropSource + 1, j + 1); // # notranslate
239239
if (k==NA_INTEGER) {
240240
if (isString(items))
241241
DTWARN(_("Column name '%s' (%s) not found"), CHAR(STRING_ELT(items, j)), buff);
@@ -264,7 +264,7 @@ bool userOverride(int8_t *type, lenOff *colNames, const char *anchor, const int
264264
SEXP elem;
265265
if (colNames==NULL || colNames[i].len<=0) {
266266
char buff[12];
267-
snprintf(buff,12,"V%d",i+1); // # notranslate
267+
snprintf(buff, sizeof(buff), "V%d", i + 1); // # notranslate
268268
elem = mkChar(buff); // no PROTECT as passed immediately to SET_STRING_ELT
269269
} else {
270270
elem = mkCharLenCE(anchor+colNames[i].off, colNames[i].len, ienc); // no PROTECT as passed immediately to SET_STRING_ELT
@@ -716,7 +716,7 @@ void halt__(bool warn, const char *format, ...) {
716716
va_list args;
717717
va_start(args, format);
718718
char msg[2000];
719-
vsnprintf(msg, 2000, format, args);
719+
vsnprintf(msg, sizeof(msg), format, args);
720720
va_end(args);
721721
freadCleanup(); // this closes mmp hence why we just copied substrings from mmp to msg[] first since mmp is now invalid
722722
// if (warn) warning(_("%s"), msg);

src/freadR.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
// Where no halt is happening, we can just use raw Rprintf() or warning()
2222
void halt__(bool warn, const char *format, ...); // see freadR.c
2323
#define STOP(...) halt__(0, __VA_ARGS__)
24-
static char internal_error_buff[1001] __attribute__((unused)); // match internalErrSize // todo: fix imports such that compiler warns correctly #6468
25-
#define INTERNAL_STOP(...) do {snprintf(internal_error_buff, 1000, __VA_ARGS__); halt__(0, "%s %s: %s. %s", _("Internal error in"), __func__, internal_error_buff, _("Please report to the data.table issues tracker"));} while (0)
24+
static char internal_error_buff[1000] __attribute__((unused)); // match internalErrSize // todo: fix imports such that compiler warns correctly #6468
25+
#define INTERNAL_STOP(...) do {snprintf(internal_error_buff, sizeof(internal_error_buff), __VA_ARGS__); halt__(0, "%s %s: %s. %s", _("Internal error in"), __func__, internal_error_buff, _("Please report to the data.table issues tracker"));} while (0)
2626
#define DTPRINT Rprintf
2727
#define DTWARN(...) warningsAreErrors ? halt__(1, __VA_ARGS__) : warning(__VA_ARGS__)
2828

src/fwrite.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
#define STOP error
1111
#define DTPRINT Rprintf
1212
static char internal_error_buff[256] __attribute__((unused)); // todo: fix imports such that compiler warns correctly #6468
13-
#define INTERNAL_STOP(...) do {snprintf(internal_error_buff, 255, __VA_ARGS__); error("%s %s: %s. %s", _("Internal error in"), __func__, internal_error_buff, _("Please report to the data.table issues tracker"));} while (0)
13+
#define INTERNAL_STOP(...) do {snprintf(internal_error_buff, sizeof(internal_error_buff), __VA_ARGS__); error("%s %s: %s. %s", _("Internal error in"), __func__, internal_error_buff, _("Please report to the data.table issues tracker"));} while (0)
1414
#endif
1515

1616
typedef void writer_fun_t(const void *, int64_t, char **);

src/rbindlist.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg, SEXP ignor
197197
if (!fill && (usenames==TRUE || usenames==NA_LOGICAL)) {
198198
// Ensure no missings in both cases, and (when usenames==NA) all columns in same order too
199199
// We proceeded earlier as if fill was true, so varying ncol items will have missing here
200-
char buff[1001] = "";
200+
char buff[1000] = "";
201201
const char *extra = usenames==TRUE?"":_(" use.names='check' (default from v1.12.2) emits this message and proceeds as if use.names=FALSE for "
202202
" backwards compatibility. See news item 5 in v1.12.2 for options to control this message.");
203203
for (int i=0; i<LENGTH(l); ++i) {
@@ -212,7 +212,7 @@ SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg, SEXP ignor
212212
SEXP s = getAttrib(VECTOR_ELT(l, i), R_NamesSymbol);
213213
int w2 = colMap[i*ncol + j];
214214
const char *str = isString(s) ? CHAR(STRING_ELT(s,w2)) : "";
215-
snprintf(buff, 1000, _("Column %d ['%s'] of item %d is missing in item %d. Use fill=TRUE to fill with NA (NULL for list columns), or use.names=FALSE to ignore column names.%s"),
215+
snprintf(buff, sizeof(buff), _("Column %d ['%s'] of item %d is missing in item %d. Use fill=TRUE to fill with NA (NULL for list columns), or use.names=FALSE to ignore column names.%s"),
216216
w2+1, str, i+1, missi+1, extra );
217217
if (usenames==TRUE) error("%s", buff); // # notranslate
218218
i = LENGTH(l); // break from outer i loop
@@ -221,7 +221,7 @@ SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg, SEXP ignor
221221
if (w!=j && usenames==NA_LOGICAL) {
222222
SEXP s = getAttrib(VECTOR_ELT(l, i), R_NamesSymbol);
223223
if (!isString(s) || i==0) internal_error(__func__, "usenames==NA but an out-of-order name has been found in an item with no names or the first item. [%d]", i); // # nocov
224-
snprintf(buff, 1000, _("Column %d ['%s'] of item %d appears in position %d in item %d. Set use.names=TRUE to match by column name, or use.names=FALSE to ignore column names.%s"),
224+
snprintf(buff, sizeof(buff), _("Column %d ['%s'] of item %d appears in position %d in item %d. Set use.names=TRUE to match by column name, or use.names=FALSE to ignore column names.%s"),
225225
w+1, CHAR(STRING_ELT(s,w)), i+1, j+1, i, extra);
226226
i = LENGTH(l);
227227
break;
@@ -336,7 +336,7 @@ SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg, SEXP ignor
336336
}
337337
}
338338

339-
if (!foundName) { static char buff[12]; snprintf(buff,12,"V%d",j+1), SET_STRING_ELT(ansNames, idcol+j, mkChar(buff)); foundName=buff; } // # notranslate
339+
if (!foundName) { static char buff[12]; snprintf(buff, sizeof(buff), "V%d", j + 1), SET_STRING_ELT(ansNames, idcol + j, mkChar(buff)); foundName = buff; } // # notranslate
340340
if (factor) maxType=INTSXP; // if any items are factors then a factor is created (could be an option)
341341
if (int64 && !(maxType==REALSXP || maxType==STRSXP || maxType==VECSXP || maxType==CPLXSXP))
342342
internal_error(__func__, "column %d of result is determined to be integer64 but maxType=='%s' != REALSXP", j+1, type2char(maxType)); // # nocov
@@ -402,12 +402,12 @@ SEXP rbindlist(SEXP l, SEXP usenamesArg, SEXP fillArg, SEXP idcolArg, SEXP ignor
402402
const int tl = TRUELENGTH(s);
403403
if (tl>=last) { // if tl>=0 then also tl>=last because last<=0
404404
if (tl>=0) {
405-
snprintf(warnStr, 1000, // not direct warning as we're inside tl region
405+
snprintf(warnStr, sizeof(warnStr), // not direct warning as we're inside tl region
406406
_("Column %d of item %d is an ordered factor but level %d ['%s'] is missing from the ordered levels from column %d of item %d. " \
407407
"Each set of ordered factor levels should be an ordered subset of the first longest. A regular factor will be created for this column."),
408408
w+1, i+1, k+1, CHAR(s), longestW+1, longestI+1);
409409
} else {
410-
snprintf(warnStr, 1000,
410+
snprintf(warnStr, sizeof(warnStr),
411411
_("Column %d of item %d is an ordered factor with '%s'<'%s' in its levels. But '%s'<'%s' in the ordered levels from column %d of item %d. " \
412412
"A regular factor will be created for this column due to this ambiguity."),
413413
w+1, i+1, CHAR(levelsD[k-1]), CHAR(s), CHAR(s), CHAR(levelsD[k-1]), longestW+1, longestI+1);

src/utils.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -412,11 +412,11 @@ SEXP coerceAs(SEXP x, SEXP as, SEXP copyArg) {
412412
#include <zlib.h>
413413
#endif
414414
SEXP dt_zlib_version(void) {
415-
char out[71];
415+
char out[70];
416416
#ifndef NOZLIB
417-
snprintf(out, 70, "zlibVersion()==%s ZLIB_VERSION==%s", zlibVersion(), ZLIB_VERSION); // # notranslate
417+
snprintf(out, sizeof(out), "zlibVersion()==%s ZLIB_VERSION==%s", zlibVersion(), ZLIB_VERSION); // # notranslate
418418
#else
419-
snprintf(out, 70, _("zlib header files were not found when data.table was compiled"));
419+
snprintf(out, sizeof(out), _("zlib header files were not found when data.table was compiled"));
420420
#endif
421421
return ScalarString(mkChar(out));
422422
}
@@ -547,7 +547,7 @@ void internal_error(const char *call_name, const char *format, ...) {
547547
va_list args;
548548
va_start(args, format);
549549

550-
vsnprintf(buff, 1023, format, args);
550+
vsnprintf(buff, sizeof(buff), format, args);
551551
va_end(args);
552552

553553
error("%s %s: %s. %s", _("Internal error in"), call_name, buff, _("Please report to the data.table issues tracker."));

src/wrappers.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ SEXP address(SEXP x)
8383
{
8484
// A better way than : http://stackoverflow.com/a/10913296/403310
8585
char buffer[32];
86-
snprintf(buffer, 32, "%p", (void *)x); // # notranslate
86+
snprintf(buffer, sizeof(buffer), "%p", (void*)x); // # notranslate
8787
return(mkString(buffer));
8888
}
8989

0 commit comments

Comments
 (0)