Tightened syntax checks to discover bad numerical arguments

This commit is contained in:
Gerhard Rieger 2022-08-12 13:19:03 +02:00
parent 88cdeed092
commit 893d031cc2
10 changed files with 233 additions and 56 deletions

View file

@ -33,6 +33,10 @@ Corrections:
Fixed bad parser error message on "socat /tmp/x\"x/x -"
Tightened syntax checks to detect numerical arguments that are missing
or have trailing garbage.
Test: INTEGER_GARBAGE
Porting:
OpenSSL, at least 1.1 on Ubuntu, crashed with SIGSEGV under certain
conditions: client connection to server with certificate with empty

View file

@ -21,7 +21,7 @@ static void filan_usage(FILE *fd);
int main(int argc, const char *argv[]) {
const char **arg1, *a;
const char **arg1, *a0, *a;
const char *filename = NULL, *waittimetxt;
unsigned int m = 0; /* first FD (default) */
unsigned int n = FD_SETSIZE; /* last excl. */
@ -50,27 +50,39 @@ int main(int argc, const char *argv[]) {
case 'S': style = arg1[0][1]; break;
case 'r': filan_rawoutput = true; break;
case 'i': if (arg1[0][2]) {
a = *arg1+2;
a = a0 = *arg1+2;
} else {
++arg1, --argc;
if ((a = *arg1) == NULL) {
if ((a = a0 = *arg1) == NULL) {
Error("option -i requires an argument");
filan_usage(stderr); exit(1);
}
}
m = strtoul(a, (char **)&a, 0);
if (a == a0) {
Error1("not a numerical arg in \"-b %s\"", a0);
}
if (*a != '\0') {
Error1("trailing garbage in \"-b %s\"", a0);
}
n = m;
break;
case 'n': if (arg1[0][2]) {
a = *arg1+2;
a = a0 = *arg1+2;
} else {
++arg1, --argc;
if ((a = *arg1) == NULL) {
if ((a = a0 = *arg1) == NULL) {
Error("option -n requires an argument");
filan_usage(stderr); exit(1);
}
}
n = strtoul(a, (char **)&a, 0);
if (a == a0) {
Error1("not a numerical arg in \"-b %s\"", a0);
}
if (*a != '\0') {
Error1("trailing garbage in \"-b %s\"", a0);
}
break;
case 'f': if (arg1[0][2]) {
filename = *arg1+2;
@ -135,7 +147,7 @@ int main(int argc, const char *argv[]) {
else if (!strcmp(outfname,"stderr")) { fdout=stderr; }
/* file descriptor */
else if (*outfname == '+') {
a = outfname+1;
a = outfname+1;
fildes = strtoul(a, (char **)&a, 0);
if ((fdout = fdopen(fildes, "w")) == NULL) {
Error2("can't fdopen file descriptor %lu: %s\n", fildes, strerror(errno));

View file

@ -238,7 +238,7 @@ int main(int argc, const char *argv[]) {
Exit(1);
}
}
socat_opts.bufsiz = strtoul(a, (char **)&a, 0);
socat_opts.bufsiz = Strtoul(a, (char **)&a, 0, "-b");
break;
case 's': if (arg1[0][2]) { socat_opt_hint(stderr, arg1[0][1], arg1[0][2]); Exit(1); }
diag_set_int('e', E_FATAL); break;
@ -251,7 +251,7 @@ int main(int argc, const char *argv[]) {
Exit(1);
}
}
rto = strtod(a, (char **)&a);
rto = Strtod(a, (char **)&a, "-t");
socat_opts.closwait.tv_sec = rto;
socat_opts.closwait.tv_usec =
(rto-socat_opts.closwait.tv_sec) * 1000000;
@ -265,7 +265,7 @@ int main(int argc, const char *argv[]) {
Exit(1);
}
}
rto = strtod(a, (char **)&a);
rto = Strtod(a, (char **)&a, "-T");
socat_opts.total_timeout.tv_sec = rto;
socat_opts.total_timeout.tv_usec =
(rto-socat_opts.total_timeout.tv_sec) * 1000000;

View file

@ -1736,6 +1736,7 @@ void Unsetenv(const char *name) {
}
#endif
#if WITH_READLINE
char *Readline(const char *prompt) {

View file

@ -167,6 +167,8 @@ void Abort(void);
int Mkstemp(char *template);
int Setenv(const char *name, const char *value, int overwrite);
void Unsetenv(const char *name);
#endif /* WITH_SYCLS */
#if WITH_SYCLS
char *Readline(const char *prompt);
void Using_history(void);

View file

@ -824,3 +824,45 @@ int xiosetenvushort(const char *varname, unsigned short value, int overwrite) {
return xiosetenv(varname, envbuff, overwrite, NULL);
# undef XIO_SHORTLEN
}
unsigned long int Strtoul(const char *nptr, char **endptr, int base, const char *txt) {
unsigned long res;
res = strtoul(nptr, endptr, base);
if (nptr == *endptr) {
Error1("parseopts(): missing numerical value of option \"%s\"", txt);
}
if (**endptr != '\0') {
Error1("parseopts(): trailing garbage in numerical arg of option \"%s\"", txt);
}
return res;
}
#if HAVE_STRTOLL
long long int Strtoll(const char *nptr, char **endptr, int base, const char *txt) {
long long int res;
res = strtoul(nptr, endptr, base);
if (nptr == *endptr) {
Error1("parseopts(): missing numerical value of option \"%s\"", txt);
}
if (**endptr != '\0') {
Error1("parseopts(): trailing garbage in numerical arg of option \"%s\"", txt);
}
return res;
}
#endif /* HAVE_STRTOLL */
double Strtod(const char *nptr, char **endptr, const char *txt) {
double res;
res = strtod(nptr, endptr);
if (nptr == *endptr) {
Error1("parseopts(): missing numerical value of option \"%s\"", txt);
}
if (**endptr != '\0') {
Error1("parseopts(): trailing garbage in numerical arg of option \"%s\"", txt);
}
return res;
}

View file

@ -107,5 +107,8 @@ extern int xiosetenvulong(const char *varname, unsigned long value,
int overwrite);
extern int xiosetenvushort(const char *varname, unsigned short value,
int overwrite);
extern unsigned long int Strtoul(const char *nptr, char **endptr, int base, const char *txt);
extern long long int Strtoll(const char *nptr, char **endptr, int base, const char *txt);
extern double Strtod(const char *nptr, char **endptr, const char *txt);
#endif /* !defined(__sysutils_h_included) */

67
test.sh
View file

@ -13886,7 +13886,7 @@ N=$((N+1))
# Test if unbalanced quoting in Socat addresses is detected
NAME=UNBALANCED_QUOTE
case "$TESTS" in
*%$N%*|*%functions%*|*%bugs%*|*%$NAME%*)
*%$N%*|*%functions%*|*%syntax%*|*%bugs%*|*%$NAME%*)
TEST="$NAME: Test fix of unbalanced quoting"
# Invoke Socat with an address containing unbalanced quoting. If Socat prints
# a "syntax error" message, the test succeeds
@ -13900,6 +13900,8 @@ printf "test $F_n $TEST... " $N
$CMD0 >/dev/null 2>"${te}0"
if grep -q "syntax error" "${te}0"; then
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0" >&2; fi
if [ "$debug" ]; then cat ${te} >&2; fi
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
@ -15537,6 +15539,69 @@ esac
PORT=$((PORT+1))
N=$((N+1))
# Test if trailing garbage in integer type options gives error
NAME=MISSING_INTEGER
case "$TESTS" in
*%$N%*|*%functions%*|*%syntax%*|*%bugs%*|*%$NAME%*)
TEST="$NAME: Error on option that's missing integer value"
# Invoke Socat with pty and option ispeed=b19200.
# When socat terminates with error the test succeeded
if ! eval $NUMCOND; then :; else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
CMD0="$TRACE $SOCAT $opts - PTY,ispeed=b19200"
printf "test $F_n $TEST... " $N
$CMD0 </dev/null >/dev/null 2>"${te}0"
if grep -q "missing numerical value" "${te}0"; then
$PRINTF "$OK\n"
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
echo "$CMD0"
cat "${te}0"
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
fi
fi # NUMCOND
;;
esac
N=$((N+1))
# Test if trailing garbage in integer type options gives error
NAME=INTEGER_GARBAGE
case "$TESTS" in
*%$N%*|*%functions%*|*%syntax%*|*%bugs%*|*%$NAME%*)
TEST="$NAME: Error on trailing garbabe"
# Invoke Socat with pty and option ispeed=b19200.
# When socat terminates with error the test succeeded
if ! eval $NUMCOND; then :; else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM"
CMD0="$TRACE $SOCAT $opts - PTY,ispeed=19200B"
printf "test $F_n $TEST... " $N
$CMD0 </dev/null >/dev/null 2>"${te}0"
if grep -q "trailing garbage" "${te}0"; then
$PRINTF "$OK\n"
if [ "$VERBOSE" ]; then echo "$CMD0" >&2; fi
if [ "$debug" ]; then cat ${te} >&2; fi
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
echo "$CMD0"
cat "${te}0"
numFAIL=$((numFAIL+1))
listFAIL="$listFAIL $N"
fi
fi # NUMCOND
;;
esac
N=$((N+1))
# end of common tests
##################################################################################

View file

@ -160,7 +160,7 @@ char *xiosubstr(char *scratch, const char *str, size_t from, size_t len) {
*scratch = '\0';
return scratch0;
}
/* since version 1.7.2.4 socat supports C-99 behaviour of snprintf but still
can handle the old glibc case with -1 return on truncation.

138
xioopts.c
View file

@ -1926,7 +1926,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
nestlex(a, &tokp, &len, endkey, hquotes, squotes, nests,
true, true, false);
if (parsres < 0) {
Error1("option too long: \"%s\"", *a);
Error1("option too long: \"%s\"", *a);
return -1;
} else if (parsres > 0) {
Error1("syntax error in \"%s\"", *a);
@ -2006,9 +2006,9 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
if (assign) {
unsigned long ul;
char *rest;
ul = strtoul(token, &rest/*!*/, 0);
ul = Strtoul(token, &rest, 0, a0);
if (ul > UCHAR_MAX) {
Error3("parseopts_table(%s): byte value exceeds limit (%lu vs. %u), using max",
Error3("parseopts(): option \"%s\": byte value exceeds limit (%lu vs. %u), using max",
a0, ul, UCHAR_MAX);
(*opts)[i].value.u_byte = UCHAR_MAX;
} else {
@ -2026,7 +2026,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
case TYPE_INT:
if (assign) {
char *rest;
(*opts)[i].value.u_int = strtoul(token, &rest/*!*/, 0);
(*opts)[i].value.u_int = Strtoul(token, &rest, 0, a0);
} else {
(*opts)[i].value.u_int = 1;
}
@ -2038,10 +2038,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
(*opts)[i].value.u_bool = 1;
} else {
char *rest;
(*opts)[i].value.u_bool = strtoul(token, &rest, 0);
if (rest && *rest) {
Error1("error in option \"%s\": \"0\" or \"1\" required", a0);
}
(*opts)[i].value.u_bool = Strtoul(token, &rest, 0, a0);
}
Info2("setting option \"%s\" to %d", ent->desc->defname,
(*opts)[i].value.u_bool);
@ -2055,11 +2052,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
(*opts)[i].value.u_uint = 1;
} else {
char *rest;
ulongval = strtoul(token, &rest/*!*/, 0);
if (ulongval > UINT_MAX) {
Error3("parseopts_table(%s): unsigned int value exceeds limit (%lu vs. %u), using max",
a0, ulongval, UINT_MAX);
}
ulongval = Strtoul(token, &rest, 0, a0);
(*opts)[i].value.u_uint = ulongval;
}
Info2("setting option \"%s\" to %u", ent->desc->defname,
@ -2074,11 +2067,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
(*opts)[i].value.u_ushort = 1;
} else {
char *rest;
ulongval = strtoul(token, &rest/*!*/, 0);
if (ulongval > USHRT_MAX) {
Error3("parseopts_table(%s): unsigned short value exceeds limit (%lu vs. %u), using max",
a0, ulongval, USHRT_MAX);
}
ulongval = Strtoul(token, &rest, 0, a0);
(*opts)[i].value.u_ushort = ulongval;
}
Info2("setting option \"%s\" to %u", ent->desc->defname,
@ -2096,7 +2085,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
(*opts)[i].value.u_long = 1;
} else {
char *rest;
slongval = strtol(token, &rest, 0);
slongval = Strtoul(token, &rest, 0, a0);
(*opts)[i].value.u_long = slongval;
}
Info2("setting option \"%s\" to %lu", ent->desc->defname,
@ -2111,7 +2100,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
(*opts)[i].value.u_ulong = 1;
} else {
char *rest;
ulongval = strtoul(token, &rest, 0);
ulongval = Strtoul(token, &rest, 0, a0);
(*opts)[i].value.u_ulong = ulongval;
}
Info2("setting option \"%s\" to %lu", ent->desc->defname,
@ -2131,11 +2120,14 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
} else {
char *rest;
# if HAVE_STRTOLL
slonglongval = strtoll(token, &rest, 0);
slonglongval = Strtoll(token, &rest, 0, a0);
# else
/* in this case, input value range is limited */
slonglongval = strtol(token, &rest, 0);
slonglongval = Strtol(token, &rest, 0, a0);
# endif /* HAVE_STRTOLL */
if (*rest != '\0') {
Error1("parseopts(): trailing garbage in numerical arg of option \"%s\"", a0);
}
(*opts)[i].value.u_longlong = slonglongval;
}
Info2("setting option \"%s\" to %Lu", ent->desc->defname,
@ -2150,7 +2142,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
}
if (isdigit((*token)&0xff)) {
char *rest;
(*opts)[i].value.u_uidt = strtoul(token, &rest/*!*/, 0);
(*opts)[i].value.u_uidt = Strtoul(token, &rest, 0, a0);
} else {
struct passwd *pwd;
if ((pwd = getpwnam(token)) == NULL) {
@ -2168,7 +2160,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
continue; }
if (isdigit((token[0])&0xff)) {
char *rest;
(*opts)[i].value.u_gidt = strtoul(token, &rest/*!*/, 0);
(*opts)[i].value.u_gidt = Strtoul(token, &rest, 0, a0);
} else {
struct group *grp;
grp = getgrnam(token);
@ -2188,7 +2180,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
}
{
char *rest;
(*opts)[i].value.u_modet = strtoul(token, &rest/*!*/, 8);
(*opts)[i].value.u_modet = Strtoul(token, &rest, 8, a0);
}
Info2("setting option \"%s\" to %u", ent->desc->defname,
(*opts)[i].value.u_modet);
@ -2229,7 +2221,8 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
continue;
} else {
double val;
val = strtod(token, NULL);
char *rest;
val = Strtod(token, &rest, a0);
if (val == HUGE_VAL || val == -HUGE_VAL ||
val == 0.0 && errno == ERANGE) {
Error2("strtod(\"%s\", NULL): %s", token, strerror(errno));
@ -2248,7 +2241,8 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
continue;
} else {
double val;
val = strtod(token, NULL);
char *rest;
val = Strtod(token, &rest, a0);
if (val == HUGE_VAL || val == -HUGE_VAL ||
val == 0.0 && errno == ERANGE) {
Error2("strtod(\"%s\", NULL): %s", token, strerror(errno));
@ -2270,7 +2264,7 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
(*opts)[i].value.u_linger.l_onoff = 1;
{
char *rest;
(*opts)[i].value.u_linger.l_linger = strtoul(token, &rest/*!*/, 0);
(*opts)[i].value.u_linger.l_linger = Strtoul(token, &rest, 0, a0);
}
Info3("setting option \"%s\" to {%d,%d}", ent->desc->defname,
(*opts)[i].value.u_linger.l_onoff,
@ -2287,12 +2281,15 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
{
char *rest;
(*opts)[i].value.u_int = strtoul(token, &rest, 0);
if (*rest != ':') {
Error1("option \"%s\": 2 arguments required",
if (token == rest) {
Error1("parseopts(): missing numerical value of option \"%s\"", a0);
}
if (*rest == '\0') {
Error1("parseopts(): option \"%s\": 2 arguments required",
ent->desc->defname);
}
++rest;
(*opts)[i].value2.u_int = strtoul(rest, &rest, 0);
(*opts)[i].value2.u_int = Strtoul(rest, &rest, 0, a0);
}
Info3("setting option \"%s\" to %d:%d", ent->desc->defname,
(*opts)[i].value.u_int, (*opts)[i].value2.u_int);
@ -2306,8 +2303,14 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
{
char *rest;
(*opts)[i].value.u_int = strtoul(token, &rest, 0);
if (token == rest) {
Error1("parseopts(): missing numerical value of option \"%s\"", a0);
}
if (*rest != ':') {
Error1("option \"%s\": 2 arguments required",
Error1("parseopts(): trailing garbage in numerical arg of option \"%s\"", a0);
}
if (*rest == '\0') {
Error1("parseopts(): option \"%s\": 2 arguments required",
ent->desc->defname);
}
++rest;
@ -2334,8 +2337,14 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
{
char *rest;
(*opts)[i].value.u_int = strtoul(token, &rest, 0);
if (token == rest) {
Error1("parseopts(): missing numerical value of option \"%s\"", a0);
}
if (*rest != ':') {
Error1("option \"%s\": 2 arguments required",
Error1("parseopts(): trailing garbage in numerical arg of option \"%s\"", a0);
}
if (*rest == '\0') {
Error1("parseopts(): option \"%s\": 2 arguments required",
ent->desc->defname);
}
++rest;
@ -2355,18 +2364,28 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
{
char *rest;
(*opts)[i].value.u_int = strtoul(token, &rest, 0);
if (token == rest) {
Error1("parseopts(): missing numerical value of option \"%s\"", a0);
}
if (*rest == '\0') {
Error1("parseopts(): option \"%s\": 3 arguments required", ent->desc->defname);
}
if (*rest != ':') {
Error1("option \"%s\": 3 arguments required",
ent->desc->defname);
Error1("parseopts(): trailing garbage in 1st numerical arg of option \"%s\"", a0);
}
++rest;
(*opts)[i].value2.u_int = strtoul(rest, &rest, 0);
if (token == rest) {
Error1("parseopts(): missing numerical value of option \"%s\"", a0);
}
if (*rest == '\0') {
Error1("parseopts(): option \"%s\": 3 arguments required", ent->desc->defname);
}
if (*rest != ':') {
Error1("option \"%s\": 3 arguments required",
ent->desc->defname);
Error1("parseopts(): trailing garbage in 2nd numerical arg of option \"%s\"", a0);
}
++rest;
(*opts)[i].value3.u_int = strtoul(rest, &rest, 0);
(*opts)[i].value3.u_int = Strtoul(rest, &rest, 0, a0);
}
Info4("setting option \"%s\" to %d:%d:%d", ent->desc->defname,
(*opts)[i].value.u_int, (*opts)[i].value2.u_int, (*opts)[i].value3.u_int);
@ -2381,16 +2400,28 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
{
char *rest;
(*opts)[i].value.u_int = strtoul(token, &rest, 0);
if (*rest != ':') {
if (token == rest) {
Error1("parseopts(): missing numerical value of option \"%s\"", a0);
}
if (*rest == '\0') {
Error1("option \"%s\": 3 arguments required",
ent->desc->defname);
}
if (*rest != ':') {
Error1("parseopts(): trailing garbage in 1st numerical arg of option \"%s\"", a0);
}
++rest;
(*opts)[i].value2.u_int = strtoul(rest, &rest, 0);
if (*rest != ':') {
if (token == rest) {
Error1("parseopts(): missing numerical value of option \"%s\"", a0);
}
if (*rest == '\0') {
Error1("option \"%s\": 3 arguments required",
ent->desc->defname);
}
if (*rest != ':') {
Error1("parseopts(): trailing garbage in 2nd numerical arg of option \"%s\"", a0);
}
++rest;
optlen = 0;
if ((result = dalan(rest, optbuf, &optlen, sizeof(optbuf), 'i')) != 0) {
@ -2415,16 +2446,28 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts,
{
char *rest;
(*opts)[i].value.u_int = strtoul(token, &rest, 0);
if (*rest != ':') {
Error1("option \"%s\": 3 arguments required",
if (token == rest) {
Error1("parseopts(): missing numerical value of option \"%s\"", a0);
}
if (*rest == '\0') {
Error1("parseopts(): option \"%s\": 3 arguments required",
ent->desc->defname);
}
if (*rest != ':') {
Error1("parseopts(): trailing garbage in 1st numerical arg of option \"%s\"", a0);
}
++rest;
(*opts)[i].value2.u_int = strtoul(rest, &rest, 0);
if (*rest != ':') {
Error1("option \"%s\": 3 arguments required",
if (token == rest) {
Error1("parseopts(): missing numerical value of option \"%s\"", a0);
}
if (*rest == '\0') {
Error1("parseopts(): option \"%s\": 3 arguments required",
ent->desc->defname);
}
if (*rest != ':') {
Error1("parseopts(): trailing garbage in 2nd numerical arg of option \"%s\"", a0);
}
++rest;
if (((*opts)[i].value3.u_string = strdup(rest)) == NULL) {
Error("out of memory"); return -1;
@ -2873,9 +2916,14 @@ int retropt_int(struct opt *opts, int optcode, int *result) {
while (opt->desc != ODESC_END) {
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
char *rest;
switch (opt->desc->type) {
case TYPE_INT: *result = opt->value.u_int; break;
case TYPE_STRING: *result = strtol(opt->value.u_string, NULL, 0);
case TYPE_STRING: *result = strtol(opt->value.u_string, &rest, 0);
if (*rest != '\0') {
Error1("retropts: trailing garbage in numerical arg of option \"%s\"",
opt->desc->defname);
}
break;
default: Error2("cannot convert type %d of option %s to int",
opt->desc->type, opt->desc->defname);