#include "smatch.h"
#include "smatch_slist.h"
#include "smatch_extra.h"
__ALLOCATOR(sval_t, "svals", sval);
sval_t *sval_alloc(sval_t sval)
{
sval_t *ret;
ret = __alloc_sval(0);
*ret = sval;
return ret;
}
sval_t *sval_alloc_permanent(sval_t sval)
{
sval_t *ret;
ret = malloc(sizeof(*ret));
*ret = sval;
return ret;
}
sval_t sval_blank(struct expression *expr)
{
sval_t ret;
ret.type = get_type(expr);
if (!ret.type)
ret.type = &int_ctype;
ret.value = 123456789;
return ret;
}
sval_t sval_type_val(struct symbol *type, long long val)
{
sval_t ret;
if (!type)
type = &llong_ctype;
ret.type = type;
ret.value = val;
return ret;
}
sval_t sval_type_fval(struct symbol *type, long double fval)
{
sval_t ret;
ret.type = &ldouble_ctype;
ret.ldvalue = fval;
return sval_cast(type, ret);
}
sval_t sval_from_val(struct expression *expr, long long val)
{
sval_t ret;
ret = sval_blank(expr);
ret.value = val;
ret = sval_cast(get_type(expr), ret);
return ret;
}
sval_t sval_from_fval(struct expression *expr, long double fval)
{
sval_t ret;
ret.type = &ldouble_ctype;
ret.ldvalue = fval;
ret = sval_cast(get_type(expr), ret);
return ret;
}
int sval_is_ptr(sval_t sval)
{
if (!sval.type)
return 0;
return (sval.type->type == SYM_PTR || sval.type->type == SYM_ARRAY);
}
bool sval_is_fp(sval_t sval)
{
return type_is_fp(sval.type);
}
int sval_unsigned(sval_t sval)
{
if (is_ptr_type(sval.type))
return true;
return type_unsigned(sval.type);
}
int sval_signed(sval_t sval)
{
return !type_unsigned(sval.type);
}
int sval_bits(sval_t sval)
{
return type_bits(sval.type);
}
int sval_bits_used(sval_t sval)
{
int i;
for (i = 64; i >= 1; i--) {
if (sval.uvalue & (1ULL << (i - 1)))
return i;
}
return 0;
}
int sval_is_negative(sval_t sval)
{
if (type_unsigned(sval.type))
return 0;
if (sval.value < 0)
return 1;
return 0;
}
int sval_is_positive(sval_t sval)
{
return !sval_is_negative(sval);
}
static bool fp_is_min(sval_t sval)
{
if (sval.type == &float_ctype)
return sval.fvalue == -FLT_MAX;
if (sval.type == &double_ctype)
return sval.dvalue == -DBL_MAX;
if (sval.type == &ldouble_ctype)
return sval.ldvalue == -LDBL_MAX;
sm_perror("%s: bad type: '%s'", __func__, type_to_str(sval.type));
return false;
}
int sval_is_min(sval_t sval)
{
sval_t min = sval_type_min(sval.type);
if (sval_is_fp(sval))
return fp_is_min(sval);
if (sval_unsigned(sval)) {
if (sval.uvalue == 0)
return 1;
return 0;
}
return (sval.value <= min.value);
}
static bool fp_is_max(sval_t sval)
{
if (sval.type == &float_ctype)
return sval.fvalue == FLT_MAX;
if (sval.type == &double_ctype)
return sval.dvalue == DBL_MAX;
if (sval.type == &ldouble_ctype)
return sval.ldvalue == LDBL_MAX;
sm_perror("%s: bad type: '%s'", __func__, type_to_str(sval.type));
return false;
}
int sval_is_max(sval_t sval)
{
sval_t max = sval_type_max(sval.type);
if (sval_is_fp(sval))
return fp_is_max(sval);
if (sval_unsigned(sval))
return (sval.uvalue >= max.value);
return (sval.value >= max.value);
}
int sval_is_a_min(sval_t sval)
{
if (sval_is_min(sval))
return 1;
if (sval_is_fp(sval))
return 0;
if (sval_signed(sval) && sval.value == SHRT_MIN)
return 1;
if (sval_signed(sval) && sval.value == INT_MIN)
return 1;
if (sval_signed(sval) && sval.value == LLONG_MIN)
return 1;
return 0;
}
int sval_is_a_max(sval_t sval)
{
if (sval_is_max(sval))
return 1;
if (sval_is_fp(sval))
return 0;
if (sval.uvalue == SHRT_MAX)
return 1;
if (sval.uvalue == INT_MAX)
return 1;
if (sval.uvalue == LLONG_MAX)
return 1;
if (sval.uvalue == USHRT_MAX)
return 1;
if (sval.uvalue == UINT_MAX)
return 1;
if (sval_unsigned(sval) && sval.uvalue == ULLONG_MAX)
return 1;
if (sval.value > valid_ptr_max - 1000 &&
sval.value < valid_ptr_max + 1000)
return 1;
return 0;
}
int sval_is_negative_min(sval_t sval)
{
if (sval_is_fp(sval))
return 0;
if (!sval_is_negative(sval))
return 0;
return sval_is_min(sval);
}
int sval_cmp_t(struct symbol *type, sval_t one, sval_t two)
{
sval_t one_cast, two_cast;
one_cast = sval_cast(type, one);
two_cast = sval_cast(type, two);
return sval_cmp(one_cast, two_cast);
}
int sval_cmp_val(sval_t one, long long val)
{
sval_t sval;
sval = sval_type_val(&llong_ctype, val);
return sval_cmp(one, sval);
}
sval_t sval_min(sval_t one, sval_t two)
{
if (sval_cmp(one, two) > 0)
return two;
return one;
}
sval_t sval_max(sval_t one, sval_t two)
{
if (sval_cmp(one, two) < 0)
return two;
return one;
}
int sval_too_low(struct symbol *type, sval_t sval)
{
if (sval_is_negative(sval) && type_unsigned(type))
return 1;
if (type_signed(type) && sval_unsigned(sval))
return 0;
if (type_signed(sval.type) &&
sval.value < sval_type_min(type).value)
return 1;
if (sval_cmp(sval, sval_type_min(type)) < 0)
return 1;
return 0;
}
int sval_too_high(struct symbol *type, sval_t sval)
{
if (sval_is_negative(sval))
return 0;
if (sval.uvalue > sval_type_max(type).uvalue)
return 1;
return 0;
}
int sval_fits(struct symbol *type, sval_t sval)
{
if (type_is_fp(type))
return 1;
if (type_is_fp(sval.type))
return 0;
if (sval_too_low(type, sval))
return 0;
if (sval_too_high(type, sval))
return 0;
return 1;
}
static sval_t cast_to_fp(struct symbol *type, sval_t sval)
{
sval_t ret = {};
ret.type = type;
if (type == &float_ctype) {
if (!sval_is_fp(sval)) {
if (sval_unsigned(sval))
ret.fvalue = sval.uvalue;
else
ret.fvalue = sval.value;
} else if (sval.type == &float_ctype)
ret.fvalue = sval.fvalue;
else if (sval.type == &double_ctype)
ret.fvalue = sval.dvalue;
else
ret.fvalue = sval.ldvalue;
} else if (type == &double_ctype) {
if (!sval_is_fp(sval)) {
if (sval_unsigned(sval))
ret.dvalue = sval.uvalue;
else
ret.dvalue = sval.value;
} else if (sval.type == &float_ctype)
ret.dvalue = sval.fvalue;
else if (sval.type == &double_ctype)
ret.dvalue = sval.dvalue;
else
ret.dvalue = sval.ldvalue;
} else if (type == &ldouble_ctype) {
if (!sval_is_fp(sval)) {
if (sval_unsigned(sval))
ret.ldvalue = (long double)sval.uvalue;
else
ret.ldvalue = (long double)sval.value;
} else if (sval.type == &float_ctype)
ret.ldvalue = sval.fvalue;
else if (sval.type == &double_ctype)
ret.ldvalue = sval.dvalue;
else
ret.ldvalue = sval.ldvalue;
} else {
sm_perror("%s: bad type: %s", __func__, type_to_str(type));
}
return ret;
}
static sval_t cast_from_fp(struct symbol *type, sval_t sval)
{
sval_t ret = {};
ret.type = &llong_ctype;
if (sval.type == &float_ctype)
ret.value = sval.fvalue;
else if (sval.type == &double_ctype)
ret.value = sval.dvalue;
else if (sval.type == &ldouble_ctype)
ret.value = sval.ldvalue;
else
sm_perror("%s: bad type: %s", __func__, type_to_str(type));
return sval_cast(type, ret);
}
sval_t sval_cast(struct symbol *type, sval_t sval)
{
sval_t ret;
if (!type)
type = &int_ctype;
if (type_is_fp(type))
return cast_to_fp(type, sval);
if (type_is_fp(sval.type))
return cast_from_fp(type, sval);
ret.type = type;
switch (sval_bits(ret)) {
case 1:
ret.value = !!sval.value;
break;
case 8:
if (sval_unsigned(ret))
ret.value = (long long)(unsigned char)sval.value;
else
ret.value = (long long)(char)sval.value;
break;
case 16:
if (sval_unsigned(ret))
ret.value = (long long)(unsigned short)sval.value;
else
ret.value = (long long)(short)sval.value;
break;
case 32:
if (sval_unsigned(ret))
ret.value = (long long)(unsigned int)sval.value;
else
ret.value = (long long)(int)sval.value;
break;
default:
ret.value = sval.value;
}
return ret;
}
sval_t sval_preop(sval_t sval, int op)
{
switch (op) {
case '!':
sval.value = !sval.value;
break;
case '~':
sval.value = ~sval.value;
sval = sval_cast(sval.type, sval);
break;
case '-':
sval.value = -sval.value;
sval = sval_cast(sval.type, sval);
break;
}
return sval;
}
static sval_t sval_binop_unsigned(struct symbol *type, sval_t left, int op, sval_t right)
{
sval_t ret;
ret.type = type;
switch (op) {
case '*':
ret.uvalue = left.uvalue * right.uvalue;
break;
case '/':
if (right.uvalue == 0) {
sm_debug("%s: divide by zero", __func__);
ret.uvalue = 123456789;
} else {
ret.uvalue = left.uvalue / right.uvalue;
}
break;
case '+':
ret.uvalue = left.uvalue + right.uvalue;
break;
case '-':
ret.uvalue = left.uvalue - right.uvalue;
break;
case '%':
if (right.uvalue == 0) {
sm_perror(" %s: MOD by zero", __func__);
ret.uvalue = 123456789;
} else {
ret.uvalue = left.uvalue % right.uvalue;
}
break;
case '|':
ret.uvalue = left.uvalue | right.uvalue;
break;
case '&':
ret.uvalue = left.uvalue & right.uvalue;
break;
case SPECIAL_RIGHTSHIFT:
ret.uvalue = left.uvalue >> right.uvalue;
break;
case SPECIAL_LEFTSHIFT:
ret.uvalue = left.uvalue << right.uvalue;
break;
case '^':
ret.uvalue = left.uvalue ^ right.uvalue;
break;
default:
sm_perror(" %s: unhandled binop %s", __func__,
show_special(op));
ret.uvalue = 1234567;
}
return ret;
}
static sval_t sval_binop_signed(struct symbol *type, sval_t left, int op, sval_t right)
{
sval_t ret;
ret.type = type;
switch (op) {
case '*':
ret.value = left.value * right.value;
break;
case '/':
if (right.value == 0) {
sm_debug("%s: divide by zero", __func__);
ret.value = 123456789;
} else if (left.value == LLONG_MIN && right.value == -1) {
sm_debug("%s: invalid divide LLONG_MIN/-1", __func__);
ret.value = 12345678;
} else {
ret.value = left.value / right.value;
}
break;
case '+':
ret.value = left.value + right.value;
break;
case '-':
ret.value = left.value - right.value;
break;
case '%':
if (right.value == 0) {
sm_perror(" %s: MOD by zero", __func__);
ret.value = 123456789;
} else {
ret.value = left.value % right.value;
}
break;
case '|':
ret.value = left.value | right.value;
break;
case '&':
ret.value = left.value & right.value;
break;
case SPECIAL_RIGHTSHIFT:
ret.value = left.value >> right.value;
break;
case SPECIAL_LEFTSHIFT:
ret.value = left.value << right.value;
break;
case '^':
ret.value = left.value ^ right.value;
break;
default:
sm_perror(" %s: unhandled binop %s", __func__,
show_special(op));
ret.value = 1234567;
}
return ret;
}
static sval_t ptr_binop(struct symbol *type, sval_t left, int op, sval_t right)
{
sval_t ret;
int align;
if (op != '+' && op != '-')
return sval_binop_unsigned(type, left, op, right);
ret.type = type;
if (type->type == SYM_PTR)
type = get_real_base_type(type);
align = type->ctype.alignment;
if (align <= 0)
align = 1;
if (op == '+') {
if (type_is_ptr(left.type))
ret.value = left.value + right.value * align;
else
ret.value = left.value * align + right.value;
} else {
if (!type_is_ptr(left.type)) {
left.value = -left.value;
ret = ptr_binop(type, left, '+', right);
} else if (!type_is_ptr(right.type)) {
right.value = -right.value;
ret = ptr_binop(type, left, '+', right);
} else {
ret.value = (left.value - right.value) / align;
}
}
if (op == '-')
ret.type = ssize_t_ctype;
return ret;
}
sval_t sval_binop(sval_t left, int op, sval_t right)
{
struct symbol *type;
sval_t ret;
type = get_promoted_type(left.type, right.type);
if (type_is_ptr(type))
ret = ptr_binop(type, left, op, right);
else if (type_unsigned(type))
ret = sval_binop_unsigned(type, left, op, right);
else
ret = sval_binop_signed(type, left, op, right);
return sval_cast(type, ret);
}
int sval_unop_overflows(sval_t sval, int op)
{
if (op != '-')
return 0;
if (sval_positive_bits(sval) == 32 && sval.value == INT_MIN)
return 1;
if (sval_positive_bits(sval) == 64 && sval.value == LLONG_MIN)
return 1;
if (sval_is_negative(sval))
return 0;
if (sval_signed(sval))
return 0;
if (sval_bits(sval) == 32 && sval.uvalue > INT_MAX)
return 1;
if (sval_bits(sval) == 64 && sval.uvalue > LLONG_MAX)
return 1;
return 0;
}
int sval_binop_overflows(sval_t left, int op, sval_t right)
{
struct symbol *type;
sval_t max, min;
type = left.type;
if (type_positive_bits(right.type) > type_positive_bits(left.type))
type = right.type;
if (type_positive_bits(type) < 31)
type = &int_ctype;
max = sval_type_max(type);
min = sval_type_min(type);
switch (op) {
case '+':
if (sval_is_negative(left) && sval_is_negative(right)) {
if (left.value < min.value + right.value)
return 1;
return 0;
}
if (sval_is_negative(left) || sval_is_negative(right))
return 0;
if (left.uvalue > max.uvalue - right.uvalue)
return 1;
return 0;
case '*':
if (type_signed(type)) {
if (left.value == 0 || right.value == 0)
return 0;
if (left.value > max.value / right.value)
return 1;
if (left.value == -1 || right.value == -1)
return 0;
return left.value != left.value * right.value / right.value;
}
return right.uvalue != 0 && left.uvalue > max.uvalue / right.uvalue;
case '-':
if (type_unsigned(type)) {
if (sval_cmp(left, right) < 0)
return 1;
return 0;
}
if (sval_is_negative(left) && sval_is_negative(right))
return 0;
if (sval_is_negative(left)) {
if (left.value < min.value + right.value)
return 1;
return 0;
}
if (sval_is_negative(right)) {
if (right.value == min.value)
return 1;
right = sval_preop(right, '-');
if (sval_binop_overflows(left, '+', right))
return 1;
return 0;
}
return 0;
case SPECIAL_LEFTSHIFT:
if (sval_cmp(left, sval_binop(max, invert_op(op), right)) > 0)
return 1;
return 0;
}
return 0;
}
int sval_binop_overflows_no_sign(sval_t left, int op, sval_t right)
{
struct symbol *type;
type = left.type;
if (type_positive_bits(right.type) > type_positive_bits(left.type))
type = right.type;
if (type_positive_bits(type) <= 31)
type = &uint_ctype;
else
type = &ullong_ctype;
left = sval_cast(type, left);
right = sval_cast(type, right);
return sval_binop_overflows(left, op, right);
}
int find_first_zero_bit(unsigned long long uvalue)
{
int i;
for (i = 0; i < 64; i++) {
if (!(uvalue & (1ULL << i)))
return i;
}
return i;
}
int sm_fls64(unsigned long long uvalue)
{
int high_bit = 0;
while (uvalue) {
uvalue >>= 1;
high_bit++;
}
return high_bit;
}
unsigned long long fls_mask(unsigned long long uvalue)
{
int high_bit = 0;
high_bit = sm_fls64(uvalue);
if (high_bit == 0)
return 0;
return ((unsigned long long)-1) >> (64 - high_bit);
}
unsigned long long sval_fls_mask(sval_t sval)
{
return fls_mask(sval.uvalue);
}
static char *fp_to_str(sval_t sval)
{
char buf[32];
if (sval.type == &float_ctype)
snprintf(buf, sizeof(buf), "%f", sval.fvalue);
else if (sval.type == &double_ctype)
snprintf(buf, sizeof(buf), "%e", sval.dvalue);
else if (sval.type == &ldouble_ctype) {
snprintf(buf, sizeof(buf), "%Lf", sval.ldvalue);
} else
snprintf(buf, sizeof(buf), "nan");
return alloc_sname(buf);
}
const char *sval_to_str(sval_t sval)
{
char buf[32];
if (sval_is_fp(sval))
return fp_to_str(sval);
if (sval_is_ptr(sval) && sval.value == valid_ptr_max)
return "ptr_max";
if (sval_unsigned(sval) && sval.value == ULLONG_MAX)
return "u64max";
if (sval_unsigned(sval) && sval.value == UINT_MAX)
return "u32max";
if (sval.value == USHRT_MAX)
return "u16max";
if (sval_signed(sval) && sval.value == LLONG_MAX)
return "s64max";
if (sval.value == INT_MAX)
return "s32max";
if (sval.value == SHRT_MAX)
return "s16max";
if (sval_signed(sval) && sval.value == SHRT_MIN)
return "s16min";
if (sval_signed(sval) && sval.value == INT_MIN)
return "s32min";
if (sval_signed(sval) && sval.value == LLONG_MIN)
return "s64min";
if (sval_unsigned(sval))
snprintf(buf, sizeof(buf), "%llu", sval.value);
else if (sval.value < 0)
snprintf(buf, sizeof(buf), "(%lld)", sval.value);
else
snprintf(buf, sizeof(buf), "%lld", sval.value);
return alloc_sname(buf);
}
const char *sval_to_str_or_err_ptr(sval_t sval)
{
char buf[12];
if (option_project != PROJ_KERNEL ||
!is_ptr_type(sval.type))
return sval_to_str(sval);
if (!sval_is_fp(sval) && sval.uvalue >= -4905ULL) {
snprintf(buf, sizeof(buf), "(%lld)", sval.value);
return alloc_sname(buf);
}
return sval_to_str(sval);
}
const char *sval_to_numstr(sval_t sval)
{
char buf[30];
if (type_is_fp(sval.type))
return fp_to_str(sval);
if (sval_unsigned(sval))
snprintf(buf, sizeof(buf), "%llu", sval.value);
else if (sval.value < 0)
snprintf(buf, sizeof(buf), "(%lld)", sval.value);
else
snprintf(buf, sizeof(buf), "%lld", sval.value);
return alloc_sname(buf);
}
sval_t ll_to_sval(long long val)
{
sval_t ret;
ret.type = &llong_ctype;
ret.value = val;
return ret;
}
static void free_svals(struct symbol *sym)
{
if (__inline_fn)
return;
clear_sval_alloc();
}
void register_sval(int my_id)
{
add_hook(&free_svals, AFTER_FUNC_HOOK);
}