use super::State;
use crate::abstract_domain::AbstractDomain;
use crate::abstract_domain::BitvectorDomain;
use crate::abstract_domain::DataDomain;
use crate::abstract_domain::SizedDomain;
use crate::intermediate_representation::Project;
use crate::{
analysis::function_signature::AccessPattern, intermediate_representation::ExternSymbol,
prelude::*,
};
use std::collections::BTreeMap;
pub fn generate_param_access_stubs() -> BTreeMap<&'static str, Vec<AccessPattern>> {
let read = || AccessPattern::new().with_read_flag();
let deref = || {
AccessPattern::new()
.with_read_flag()
.with_dereference_flag()
};
let deref_mut = || {
AccessPattern::new()
.with_read_flag()
.with_dereference_flag()
.with_mutably_dereferenced_flag()
};
BTreeMap::from([
("abort", vec![]),
("atoi", vec![deref()]),
("bind", vec![read(), deref(), read()]),
("calloc", vec![read(), read()]),
("close", vec![read()]),
("connect", vec![read(), deref(), read()]),
("exit", vec![read()]),
("fclose", vec![deref_mut()]),
("fflush", vec![deref_mut()]),
("fgets", vec![deref_mut(), read(), deref_mut()]),
("fopen", vec![deref(), deref()]),
("fork", vec![]),
("fprintf", vec![deref_mut(), deref()]),
("fputc", vec![read(), deref_mut()]),
("fputs", vec![deref(), deref_mut()]),
("fread", vec![deref_mut(), read(), read(), deref_mut()]),
("free", vec![deref_mut()]),
("fwrite", vec![deref(), read(), read(), deref_mut()]),
("getenv", vec![deref()]), ("getpid", vec![]),
("getppid", vec![]),
("gettimeofday", vec![deref_mut(), deref_mut()]),
("kill", vec![read(), read()]),
("localtime", vec![deref()]), ("malloc", vec![read()]),
("memcmp", vec![deref(), deref(), read()]),
("memcpy", vec![deref_mut(), deref(), read()]),
("memmove", vec![deref_mut(), deref(), read()]),
("memset", vec![deref_mut(), read(), read()]),
("open", vec![deref(), read(), read()]),
("open64", vec![deref(), read(), read()]),
("perror", vec![deref()]),
("printf", vec![deref()]),
("putchar", vec![read()]),
("puts", vec![deref()]),
("qsort", vec![deref_mut(), read(), read(), deref()]),
("raise", vec![]),
("read", vec![read(), deref_mut(), read()]),
("realloc", vec![deref_mut(), read()]),
("recv", vec![read(), deref_mut(), read(), read()]),
(
"recvfrom",
vec![
read(),
deref_mut(),
read(),
read(),
deref_mut(),
deref_mut(),
],
),
(
"select",
vec![read(), deref_mut(), deref_mut(), deref_mut(), deref()],
),
(
"sendto",
vec![read(), deref(), read(), read(), deref(), read()],
),
(
"setsockopt",
vec![read(), read(), read(), deref_mut(), read()],
), ("signal", vec![read(), read()]),
("sleep", vec![read()]),
("snprintf", vec![deref_mut(), read(), deref()]),
("socket", vec![read(), read(), read()]),
("sprintf", vec![deref_mut(), deref()]),
("sscanf", vec![deref(), deref()]),
("strcasecmp", vec![deref(), deref()]),
("strcat", vec![deref_mut(), deref()]),
("strchr", vec![deref(), read()]),
("strcmp", vec![deref(), deref()]),
("strcpy", vec![deref_mut(), deref()]),
("strdup", vec![deref()]),
("strerror", vec![read()]),
("strlen", vec![deref()]),
("strncasecmp", vec![deref(), deref(), read()]),
("strncat", vec![deref_mut(), deref(), read()]),
("strncmp", vec![deref(), deref(), read()]),
("strncpy", vec![deref_mut(), deref(), read()]),
("strrchr", vec![deref(), read()]),
("strstr", vec![deref(), deref()]),
("strtol", vec![deref(), deref_mut(), read()]), ("strtoul", vec![deref(), deref_mut(), read()]), ("system", vec![deref()]),
("time", vec![deref_mut()]),
("unlink", vec![deref()]),
("vfprintf", vec![deref_mut(), deref(), deref()]),
("write", vec![read(), deref(), read()]),
])
}
pub fn get_stubbed_variadic_symbols() -> BTreeMap<&'static str, (usize, AccessPattern)> {
let deref = || {
AccessPattern::new()
.with_read_flag()
.with_dereference_flag()
};
let deref_mut = || {
AccessPattern::new()
.with_read_flag()
.with_dereference_flag()
.with_mutably_dereferenced_flag()
};
BTreeMap::from([
("fprintf", (1, deref())),
("printf", (0, deref())),
("snprintf", (2, deref())),
("sprintf", (1, deref())),
("sscanf", (1, deref_mut())),
])
}
pub fn compute_return_value_for_stubbed_function(
project: &Project,
state: &mut State,
extern_symbol: &ExternSymbol,
call_tid: &Tid,
) -> DataDomain<BitvectorDomain> {
use return_value_stubs::*;
match extern_symbol.name.as_str() {
"memcpy" | "memmove" | "memset" | "strcat" | "strcpy" | "strncat" | "strncpy" => {
copy_param(state, extern_symbol, 0)
}
"fgets" => or_null(copy_param(state, extern_symbol, 0)),
"calloc" | "fopen" | "malloc" | "strdup" => {
or_null(new_mem_object_id(call_tid, &extern_symbol.return_values[0]))
}
"realloc" => or_null(
copy_param(state, extern_symbol, 0).merge(&new_mem_object_id(
call_tid,
&extern_symbol.return_values[0],
)),
),
"strchr" | "strrchr" | "strstr" => {
or_null(param_plus_unknown_offset(state, extern_symbol, 0))
}
_ => untracked(project.stack_pointer_register.size),
}
}
pub mod return_value_stubs {
use crate::{abstract_domain::AbstractIdentifier, intermediate_representation::Arg};
use super::*;
pub fn untracked(register_size: ByteSize) -> DataDomain<BitvectorDomain> {
DataDomain::new_top(register_size)
}
pub fn copy_param(
state: &mut State,
extern_symbol: &ExternSymbol,
param_index: usize,
) -> DataDomain<BitvectorDomain> {
state.eval_parameter_arg(&extern_symbol.parameters[param_index])
}
pub fn new_mem_object_id(call_tid: &Tid, return_arg: &Arg) -> DataDomain<BitvectorDomain> {
DataDomain::from_target(
AbstractIdentifier::from_arg(call_tid, return_arg),
Bitvector::zero(return_arg.bytesize().into()).into(),
)
}
pub fn param_plus_unknown_offset(
state: &mut State,
extern_symbol: &ExternSymbol,
param_index: usize,
) -> DataDomain<BitvectorDomain> {
let param = state.eval_parameter_arg(&extern_symbol.parameters[param_index]);
param.add_offset(&BitvectorDomain::new_top(param.bytesize()))
}
pub fn or_null(data: DataDomain<BitvectorDomain>) -> DataDomain<BitvectorDomain> {
data.merge(&Bitvector::zero(data.bytesize().into()).into())
}
}