Check overflow in expr(1).
This commit is contained in:
parent
2a6a246ff4
commit
e4a3bb997b
173
utils/expr.c
173
utils/expr.c
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2014 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -17,8 +17,9 @@
|
||||||
* Evaluate expressions.
|
* Evaluate expressions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <error.h>
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#include <regex.h>
|
#include <regex.h>
|
||||||
|
@ -34,58 +35,71 @@
|
||||||
// `expr + 5 + + 2 +'
|
// `expr + 5 + + 2 +'
|
||||||
// TODO: Support the other GNU function extensions documented in help().
|
// TODO: Support the other GNU function extensions documented in help().
|
||||||
|
|
||||||
char* strdup_or_die(const char* str)
|
static bool integer_of_string(const char* str, intmax_t* out)
|
||||||
|
{
|
||||||
|
if ( !isdigit((unsigned char) str[0]) )
|
||||||
|
return false;
|
||||||
|
char* endptr;
|
||||||
|
errno = 0;
|
||||||
|
intmax_t result = strtoimax((char*) str, &endptr, 10);
|
||||||
|
if ( (result == INTMAX_MIN || result == INTMAX_MAX) && errno == ERANGE )
|
||||||
|
return false;
|
||||||
|
if ( *endptr )
|
||||||
|
return false;
|
||||||
|
return *out = result, true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* strdup_or_die(const char* str)
|
||||||
{
|
{
|
||||||
char* result = strdup(str);
|
char* result = strdup(str);
|
||||||
if ( !str )
|
if ( !str )
|
||||||
error(2, errno, "strdup");
|
err(2, "strdup");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* strndup_or_die(const char* str, size_t n)
|
static char* strndup_or_die(const char* str, size_t n)
|
||||||
{
|
{
|
||||||
char* result = strndup(str, n);
|
char* result = strndup(str, n);
|
||||||
if ( !str )
|
if ( !str )
|
||||||
error(2, errno, "strndup");
|
err(2, "strndup");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* print_intmax_or_die(intmax_t value)
|
static char* print_intmax_or_die(intmax_t value)
|
||||||
{
|
{
|
||||||
char value_string[sizeof(intmax_t) * 3];
|
char value_string[sizeof(intmax_t) * 3];
|
||||||
snprintf(value_string, sizeof(value_string), "%ji", value);
|
snprintf(value_string, sizeof(value_string), "%ji", value);
|
||||||
return strdup_or_die(value_string);
|
return strdup_or_die(value_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((noreturn))
|
static void syntax_error(void)
|
||||||
void syntax_error(void)
|
|
||||||
{
|
{
|
||||||
error(2, 0, "syntax error");
|
errx(2, "syntax error");
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((noreturn))
|
static void non_integer_argument(void)
|
||||||
void non_integer_argument(void)
|
|
||||||
{
|
{
|
||||||
error(2, 0, "non-integer argument");
|
errx(2, "non-integer argument");
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((noreturn))
|
static void division_by_zero(void)
|
||||||
void division_by_zero(void)
|
|
||||||
{
|
{
|
||||||
error(2, 0, "division by zero");
|
errx(2, "division by zero");
|
||||||
__builtin_unreachable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char* interpret(char** tokens, size_t num_tokens);
|
static void overflow(const char* op)
|
||||||
|
{
|
||||||
|
errx(2, "%s overflow", op);
|
||||||
|
}
|
||||||
|
|
||||||
char* interpret_left_associative(char** tokens,
|
static char* interpret(char** tokens, size_t num_tokens);
|
||||||
size_t num_tokens,
|
|
||||||
const char* operator_name,
|
static char* interpret_left_associative(char** tokens,
|
||||||
char* (*next)(char**, size_t, const void*),
|
size_t num_tokens,
|
||||||
const void* next_context,
|
const char* operator_name,
|
||||||
char* (*function)(const char*, const char*))
|
char* (*next)(char**, size_t, const void*),
|
||||||
|
const void* next_context,
|
||||||
|
char* (*function)(const char*, const char*))
|
||||||
{
|
{
|
||||||
size_t depth = 0;
|
size_t depth = 0;
|
||||||
for ( size_t n = num_tokens; n != 0; n-- )
|
for ( size_t n = num_tokens; n != 0; n-- )
|
||||||
|
@ -132,14 +146,14 @@ char* interpret_left_associative(char** tokens,
|
||||||
return next(tokens, num_tokens, next_context);
|
return next(tokens, num_tokens, next_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* bool_to_boolean_value(bool b)
|
static char* bool_to_boolean_value(bool b)
|
||||||
{
|
{
|
||||||
return strdup_or_die(b ? "1" : "0");
|
return strdup_or_die(b ? "1" : "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
char* interpret_literal(char** tokens,
|
static char* interpret_literal(char** tokens,
|
||||||
size_t num_tokens,
|
size_t num_tokens,
|
||||||
const void* ctx)
|
const void* ctx)
|
||||||
{
|
{
|
||||||
(void) ctx;
|
(void) ctx;
|
||||||
if ( num_tokens != 1 )
|
if ( num_tokens != 1 )
|
||||||
|
@ -147,9 +161,9 @@ char* interpret_literal(char** tokens,
|
||||||
return strdup_or_die(tokens[0]);
|
return strdup_or_die(tokens[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* interpret_parentheses(char** tokens,
|
static char* interpret_parentheses(char** tokens,
|
||||||
size_t num_tokens,
|
size_t num_tokens,
|
||||||
const void* ctx)
|
const void* ctx)
|
||||||
{
|
{
|
||||||
if ( 2 <= num_tokens &&
|
if ( 2 <= num_tokens &&
|
||||||
strcmp(tokens[0], "(") == 0 &&
|
strcmp(tokens[0], "(") == 0 &&
|
||||||
|
@ -158,7 +172,7 @@ char* interpret_parentheses(char** tokens,
|
||||||
return interpret_literal(tokens, num_tokens, ctx);
|
return interpret_literal(tokens, num_tokens, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_and(const char* a, const char* b)
|
static char* evaluate_and(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
if ( strcmp(a, "") != 0 && strcmp(a, "0") != 0 &&
|
if ( strcmp(a, "") != 0 && strcmp(a, "0") != 0 &&
|
||||||
strcmp(b, "") != 0 && strcmp(b, "0") != 0 )
|
strcmp(b, "") != 0 && strcmp(b, "0") != 0 )
|
||||||
|
@ -166,7 +180,7 @@ char* evaluate_and(const char* a, const char* b)
|
||||||
return strdup_or_die("0");
|
return strdup_or_die("0");
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_or(const char* a, const char* b)
|
static char* evaluate_or(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
if ( strcmp(a, "") != 0 && strcmp(a, "0") != 0 )
|
if ( strcmp(a, "") != 0 && strcmp(a, "0") != 0 )
|
||||||
return strdup_or_die(a);
|
return strdup_or_die(a);
|
||||||
|
@ -175,14 +189,11 @@ char* evaluate_or(const char* a, const char* b)
|
||||||
return strdup_or_die("0");
|
return strdup_or_die("0");
|
||||||
}
|
}
|
||||||
|
|
||||||
int compare_values(const char* a, const char* b)
|
static int compare_values(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
// TODO: Compute using arbitrary length integers.
|
intmax_t a_int, b_int;
|
||||||
char* a_endptr;
|
if ( integer_of_string(a, &a_int) &&
|
||||||
char* b_endptr;
|
integer_of_string(b, &b_int) )
|
||||||
intmax_t a_int = strtoimax((char*) a, &a_endptr, 10);
|
|
||||||
intmax_t b_int = strtoimax((char*) b, &b_endptr, 10);
|
|
||||||
if ( a[0] && !*a_endptr && b[0] && !*b_endptr )
|
|
||||||
{
|
{
|
||||||
if ( a_int < b_int )
|
if ( a_int < b_int )
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -193,104 +204,116 @@ int compare_values(const char* a, const char* b)
|
||||||
return strcoll(a, b);
|
return strcoll(a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_eq(const char* a, const char* b)
|
static char* evaluate_eq(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
return bool_to_boolean_value(compare_values(a, b) == 0);
|
return bool_to_boolean_value(compare_values(a, b) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_gt(const char* a, const char* b)
|
static char* evaluate_gt(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
return bool_to_boolean_value(0 < compare_values(a, b));
|
return bool_to_boolean_value(0 < compare_values(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_ge(const char* a, const char* b)
|
static char* evaluate_ge(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
return bool_to_boolean_value(0 <= compare_values(a, b));
|
return bool_to_boolean_value(0 <= compare_values(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_lt(const char* a, const char* b)
|
static char* evaluate_lt(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
return bool_to_boolean_value(compare_values(a, b) < 0);
|
return bool_to_boolean_value(compare_values(a, b) < 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_le(const char* a, const char* b)
|
static char* evaluate_le(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
return bool_to_boolean_value(compare_values(a, b) <= 0);
|
return bool_to_boolean_value(compare_values(a, b) <= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_neq(const char* a, const char* b)
|
static char* evaluate_neq(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
return bool_to_boolean_value(compare_values(a, b) != 0);
|
return bool_to_boolean_value(compare_values(a, b) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_integer_function(const char* a, const char* b,
|
static char* evaluate_integer_function(const char* a, const char* b,
|
||||||
intmax_t (*function)(intmax_t, intmax_t))
|
intmax_t (*function)(intmax_t, intmax_t))
|
||||||
{
|
{
|
||||||
// TODO: Compute using arbitrary length integers.
|
intmax_t a_int;
|
||||||
char* a_endptr;
|
intmax_t b_int;
|
||||||
char* b_endptr;
|
if ( !integer_of_string(a, &a_int) ||
|
||||||
intmax_t a_int = strtoimax((char*) a, &a_endptr, 10);
|
!integer_of_string(b, &b_int) )
|
||||||
intmax_t b_int = strtoimax((char*) b, &b_endptr, 10);
|
|
||||||
if ( !a[0] || *a_endptr || !b[0] || *b_endptr )
|
|
||||||
non_integer_argument();
|
non_integer_argument();
|
||||||
return print_intmax_or_die(function(a_int, b_int));
|
return print_intmax_or_die(function(a_int, b_int));
|
||||||
}
|
}
|
||||||
|
|
||||||
intmax_t integer_add(intmax_t a, intmax_t b)
|
static intmax_t integer_add(intmax_t a, intmax_t b)
|
||||||
{
|
{
|
||||||
return a + b;
|
intmax_t result = 0;
|
||||||
|
if ( __builtin_add_overflow(a, b, &result) )
|
||||||
|
overflow("addition");
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_add(const char* a, const char* b)
|
static char* evaluate_add(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
return evaluate_integer_function(a, b, integer_add);
|
return evaluate_integer_function(a, b, integer_add);
|
||||||
}
|
}
|
||||||
|
|
||||||
intmax_t integer_sub(intmax_t a, intmax_t b)
|
static intmax_t integer_sub(intmax_t a, intmax_t b)
|
||||||
{
|
{
|
||||||
return a - b;
|
intmax_t result = 0;
|
||||||
|
if ( __builtin_sub_overflow(a, b, &result) )
|
||||||
|
overflow("subtraction");
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_sub(const char* a, const char* b)
|
static char* evaluate_sub(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
return evaluate_integer_function(a, b, integer_sub);
|
return evaluate_integer_function(a, b, integer_sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
intmax_t integer_mul(intmax_t a, intmax_t b)
|
static intmax_t integer_mul(intmax_t a, intmax_t b)
|
||||||
{
|
{
|
||||||
return a * b;
|
intmax_t result = 0;
|
||||||
|
if ( __builtin_mul_overflow(a, b, &result) )
|
||||||
|
overflow("multiplication");
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_mul(const char* a, const char* b)
|
static char* evaluate_mul(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
return evaluate_integer_function(a, b, integer_mul);
|
return evaluate_integer_function(a, b, integer_mul);
|
||||||
}
|
}
|
||||||
|
|
||||||
intmax_t integer_div(intmax_t a, intmax_t b)
|
static intmax_t integer_div(intmax_t a, intmax_t b)
|
||||||
{
|
{
|
||||||
if ( b == 0 )
|
if ( b == 0 )
|
||||||
division_by_zero();
|
division_by_zero();
|
||||||
|
if ( a == INTMAX_MIN && b == -1 )
|
||||||
|
overflow("division");
|
||||||
return a / b;
|
return a / b;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_div(const char* a, const char* b)
|
static char* evaluate_div(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
return evaluate_integer_function(a, b, integer_div);
|
return evaluate_integer_function(a, b, integer_div);
|
||||||
}
|
}
|
||||||
|
|
||||||
intmax_t integer_mod(intmax_t a, intmax_t b)
|
// TODO: Is this fully well-defined?
|
||||||
|
static intmax_t integer_mod(intmax_t a, intmax_t b)
|
||||||
{
|
{
|
||||||
if ( b == 0 )
|
if ( b == 0 )
|
||||||
division_by_zero();
|
division_by_zero();
|
||||||
|
if ( a == INTMAX_MIN && b == -1 )
|
||||||
|
overflow("division");
|
||||||
return a % b;
|
return a % b;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_mod(const char* a, const char* b)
|
static char* evaluate_mod(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
return evaluate_integer_function(a, b, integer_mod);
|
return evaluate_integer_function(a, b, integer_mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* evaluate_match(const char* a, const char* b)
|
static char* evaluate_match(const char* a, const char* b)
|
||||||
{
|
{
|
||||||
regex_t regex;
|
regex_t regex;
|
||||||
int status = regcomp(®ex, b, 0);
|
int status = regcomp(®ex, b, 0);
|
||||||
|
@ -309,7 +332,7 @@ char* evaluate_match(const char* a, const char* b)
|
||||||
regerror(status, ®ex, erralloc, errbuf_needed);
|
regerror(status, ®ex, erralloc, errbuf_needed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
error(2, 0, "compiling regular expression: %s", errmsg);
|
errx(2, "compiling regular expression: %s", errmsg);
|
||||||
free(erralloc);
|
free(erralloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,9 +383,9 @@ struct binary_operator binary_operators[] =
|
||||||
{ ":", evaluate_match },
|
{ ":", evaluate_match },
|
||||||
};
|
};
|
||||||
|
|
||||||
char* interpret_binary_operator(char** tokens,
|
static char* interpret_binary_operator(char** tokens,
|
||||||
size_t num_tokens,
|
size_t num_tokens,
|
||||||
const void* context)
|
const void* context)
|
||||||
{
|
{
|
||||||
size_t index = *(const size_t*) context;
|
size_t index = *(const size_t*) context;
|
||||||
size_t next_index = index + 1;
|
size_t next_index = index + 1;
|
||||||
|
@ -386,7 +409,7 @@ char* interpret_binary_operator(char** tokens,
|
||||||
next, next_context, binop->function);
|
next, next_context, binop->function);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* interpret(char** tokens, size_t num_tokens)
|
static char* interpret(char** tokens, size_t num_tokens)
|
||||||
{
|
{
|
||||||
if ( !num_tokens )
|
if ( !num_tokens )
|
||||||
syntax_error();
|
syntax_error();
|
||||||
|
|
Loading…
Reference in New Issue