KVM: selftests: Add atoi_paranoid() to catch errors missed by atoi()

atoi() doesn't detect errors. There is no way to know that a 0 return
is correct conversion or due to an error.

Introduce atoi_paranoid() to detect errors and provide correct
conversion. Replace all atoi() calls with atoi_paranoid().

Signed-off-by: Vipin Sharma <vipinsh@google.com>
Suggested-by: David Matlack <dmatlack@google.com>
Suggested-by: Sean Christopherson <seanjc@google.com>
Reviewed-by: Sean Christopherson <seanjc@google.com>
Link: https://lore.kernel.org/r/20221103191719.1559407-4-vipinsh@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
This commit is contained in:
Vipin Sharma 2022-11-03 12:17:15 -07:00 committed by Sean Christopherson
parent 0eb88a4121
commit 018ea2d71a
14 changed files with 50 additions and 29 deletions

View File

@ -414,7 +414,7 @@ static bool parse_args(int argc, char *argv[])
while ((opt = getopt(argc, argv, "hn:i:p:m:")) != -1) { while ((opt = getopt(argc, argv, "hn:i:p:m:")) != -1) {
switch (opt) { switch (opt) {
case 'n': case 'n':
test_args.nr_vcpus = atoi(optarg); test_args.nr_vcpus = atoi_paranoid(optarg);
if (test_args.nr_vcpus <= 0) { if (test_args.nr_vcpus <= 0) {
pr_info("Positive value needed for -n\n"); pr_info("Positive value needed for -n\n");
goto err; goto err;
@ -425,21 +425,21 @@ static bool parse_args(int argc, char *argv[])
} }
break; break;
case 'i': case 'i':
test_args.nr_iter = atoi(optarg); test_args.nr_iter = atoi_paranoid(optarg);
if (test_args.nr_iter <= 0) { if (test_args.nr_iter <= 0) {
pr_info("Positive value needed for -i\n"); pr_info("Positive value needed for -i\n");
goto err; goto err;
} }
break; break;
case 'p': case 'p':
test_args.timer_period_ms = atoi(optarg); test_args.timer_period_ms = atoi_paranoid(optarg);
if (test_args.timer_period_ms <= 0) { if (test_args.timer_period_ms <= 0) {
pr_info("Positive value needed for -p\n"); pr_info("Positive value needed for -p\n");
goto err; goto err;
} }
break; break;
case 'm': case 'm':
test_args.migration_freq_ms = atoi(optarg); test_args.migration_freq_ms = atoi_paranoid(optarg);
if (test_args.migration_freq_ms < 0) { if (test_args.migration_freq_ms < 0) {
pr_info("0 or positive value needed for -m\n"); pr_info("0 or positive value needed for -m\n");
goto err; goto err;

View File

@ -423,7 +423,7 @@ int main(int argc, char *argv[])
while ((opt = getopt(argc, argv, "i:")) != -1) { while ((opt = getopt(argc, argv, "i:")) != -1) {
switch (opt) { switch (opt) {
case 'i': case 'i':
ss_iteration = atoi(optarg); ss_iteration = atoi_paranoid(optarg);
break; break;
case 'h': case 'h':
default: default:

View File

@ -824,16 +824,16 @@ int main(int argc, char **argv)
while ((opt = getopt(argc, argv, "hn:e:l:")) != -1) { while ((opt = getopt(argc, argv, "hn:e:l:")) != -1) {
switch (opt) { switch (opt) {
case 'n': case 'n':
nr_irqs = atoi(optarg); nr_irqs = atoi_paranoid(optarg);
if (nr_irqs > 1024 || nr_irqs % 32) if (nr_irqs > 1024 || nr_irqs % 32)
help(argv[0]); help(argv[0]);
break; break;
case 'e': case 'e':
eoi_split = (bool)atoi(optarg); eoi_split = (bool)atoi_paranoid(optarg);
default_args = false; default_args = false;
break; break;
case 'l': case 'l':
level_sensitive = (bool)atoi(optarg); level_sensitive = (bool)atoi_paranoid(optarg);
default_args = false; default_args = false;
break; break;
case 'h': case 'h':

View File

@ -368,7 +368,7 @@ int main(int argc, char *argv[])
params.vcpu_memory_bytes = parse_size(optarg); params.vcpu_memory_bytes = parse_size(optarg);
break; break;
case 'v': case 'v':
params.nr_vcpus = atoi(optarg); params.nr_vcpus = atoi_paranoid(optarg);
break; break;
case 'o': case 'o':
overlap_memory_access = true; overlap_memory_access = true;

View File

@ -427,7 +427,7 @@ int main(int argc, char *argv[])
p.src_type = parse_backing_src_type(optarg); p.src_type = parse_backing_src_type(optarg);
break; break;
case 'v': case 'v':
nr_vcpus = atoi(optarg); nr_vcpus = atoi_paranoid(optarg);
TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus, TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus,
"Invalid number of vcpus, must be between 1 and %d", max_vcpus); "Invalid number of vcpus, must be between 1 and %d", max_vcpus);
break; break;

View File

@ -416,7 +416,7 @@ int main(int argc, char *argv[])
run_vcpus_while_disabling_dirty_logging = true; run_vcpus_while_disabling_dirty_logging = true;
break; break;
case 'f': case 'f':
p.wr_fract = atoi(optarg); p.wr_fract = atoi_paranoid(optarg);
TEST_ASSERT(p.wr_fract >= 1, TEST_ASSERT(p.wr_fract >= 1,
"Write fraction cannot be less than one"); "Write fraction cannot be less than one");
break; break;
@ -427,7 +427,7 @@ int main(int argc, char *argv[])
help(argv[0]); help(argv[0]);
break; break;
case 'i': case 'i':
p.iterations = atoi(optarg); p.iterations = atoi_paranoid(optarg);
break; break;
case 'm': case 'm':
guest_modes_cmdline(optarg); guest_modes_cmdline(optarg);
@ -445,12 +445,12 @@ int main(int argc, char *argv[])
p.backing_src = parse_backing_src_type(optarg); p.backing_src = parse_backing_src_type(optarg);
break; break;
case 'v': case 'v':
nr_vcpus = atoi(optarg); nr_vcpus = atoi_paranoid(optarg);
TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus, TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus,
"Invalid number of vcpus, must be between 1 and %d", max_vcpus); "Invalid number of vcpus, must be between 1 and %d", max_vcpus);
break; break;
case 'x': case 'x':
p.slots = atoi(optarg); p.slots = atoi_paranoid(optarg);
break; break;
default: default:
help(argv[0]); help(argv[0]);

View File

@ -152,4 +152,6 @@ static inline void *align_ptr_up(void *x, size_t size)
return (void *)align_up((unsigned long)x, size); return (void *)align_up((unsigned long)x, size);
} }
int atoi_paranoid(const char *num_str);
#endif /* SELFTEST_KVM_TEST_UTIL_H */ #endif /* SELFTEST_KVM_TEST_UTIL_H */

View File

@ -461,7 +461,7 @@ int main(int argc, char *argv[])
p.test_mem_size = parse_size(optarg); p.test_mem_size = parse_size(optarg);
break; break;
case 'v': case 'v':
nr_vcpus = atoi(optarg); nr_vcpus = atoi_paranoid(optarg);
TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus, TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus,
"Invalid number of vcpus, must be between 1 and %d", max_vcpus); "Invalid number of vcpus, must be between 1 and %d", max_vcpus);
break; break;

View File

@ -334,3 +334,22 @@ long get_run_delay(void)
return val[1]; return val[1];
} }
int atoi_paranoid(const char *num_str)
{
char *end_ptr;
long num;
errno = 0;
num = strtol(num_str, &end_ptr, 0);
TEST_ASSERT(!errno, "strtol(\"%s\") failed", num_str);
TEST_ASSERT(num_str != end_ptr,
"strtol(\"%s\") didn't find a valid integer.", num_str);
TEST_ASSERT(*end_ptr == '\0',
"strtol(\"%s\") failed to parse trailing characters \"%s\".",
num_str, end_ptr);
TEST_ASSERT(num >= INT_MIN && num <= INT_MAX,
"%ld not in range of [%d, %d]", num, INT_MIN, INT_MAX);
return num;
}

View File

@ -193,15 +193,15 @@ int main(int argc, char *argv[])
while ((opt = getopt(argc, argv, "c:h:m:s:H")) != -1) { while ((opt = getopt(argc, argv, "c:h:m:s:H")) != -1) {
switch (opt) { switch (opt) {
case 'c': case 'c':
nr_vcpus = atoi(optarg); nr_vcpus = atoi_paranoid(optarg);
TEST_ASSERT(nr_vcpus > 0, "number of vcpus must be >0"); TEST_ASSERT(nr_vcpus > 0, "number of vcpus must be >0");
break; break;
case 'm': case 'm':
max_mem = atoi(optarg) * size_1gb; max_mem = atoi_paranoid(optarg) * size_1gb;
TEST_ASSERT(max_mem > 0, "memory size must be >0"); TEST_ASSERT(max_mem > 0, "memory size must be >0");
break; break;
case 's': case 's':
slot_size = atoi(optarg) * size_1gb; slot_size = atoi_paranoid(optarg) * size_1gb;
TEST_ASSERT(slot_size > 0, "slot size must be >0"); TEST_ASSERT(slot_size > 0, "slot size must be >0");
break; break;
case 'H': case 'H':

View File

@ -158,7 +158,7 @@ int main(int argc, char *argv[])
guest_modes_cmdline(optarg); guest_modes_cmdline(optarg);
break; break;
case 'd': case 'd':
p.memslot_modification_delay = strtoul(optarg, NULL, 0); p.memslot_modification_delay = atoi_paranoid(optarg);
TEST_ASSERT(p.memslot_modification_delay >= 0, TEST_ASSERT(p.memslot_modification_delay >= 0,
"A negative delay is not supported."); "A negative delay is not supported.");
break; break;
@ -166,7 +166,7 @@ int main(int argc, char *argv[])
guest_percpu_mem_size = parse_size(optarg); guest_percpu_mem_size = parse_size(optarg);
break; break;
case 'v': case 'v':
nr_vcpus = atoi(optarg); nr_vcpus = atoi_paranoid(optarg);
TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus, TEST_ASSERT(nr_vcpus > 0 && nr_vcpus <= max_vcpus,
"Invalid number of vcpus, must be between 1 and %d", "Invalid number of vcpus, must be between 1 and %d",
max_vcpus); max_vcpus);
@ -175,7 +175,7 @@ int main(int argc, char *argv[])
p.partition_vcpu_memory_access = false; p.partition_vcpu_memory_access = false;
break; break;
case 'i': case 'i':
p.nr_memslot_modifications = atoi(optarg); p.nr_memslot_modifications = atoi_paranoid(optarg);
break; break;
case 'h': case 'h':
default: default:

View File

@ -885,21 +885,21 @@ static bool parse_args(int argc, char *argv[],
map_unmap_verify = true; map_unmap_verify = true;
break; break;
case 's': case 's':
targs->nslots = atoi(optarg); targs->nslots = atoi_paranoid(optarg);
if (targs->nslots <= 0 && targs->nslots != -1) { if (targs->nslots <= 0 && targs->nslots != -1) {
pr_info("Slot count cap has to be positive or -1 for no cap\n"); pr_info("Slot count cap has to be positive or -1 for no cap\n");
return false; return false;
} }
break; break;
case 'f': case 'f':
targs->tfirst = atoi(optarg); targs->tfirst = atoi_paranoid(optarg);
if (targs->tfirst < 0) { if (targs->tfirst < 0) {
pr_info("First test to run has to be non-negative\n"); pr_info("First test to run has to be non-negative\n");
return false; return false;
} }
break; break;
case 'e': case 'e':
targs->tlast = atoi(optarg); targs->tlast = atoi_paranoid(optarg);
if (targs->tlast < 0 || targs->tlast >= NTESTS) { if (targs->tlast < 0 || targs->tlast >= NTESTS) {
pr_info("Last test to run has to be non-negative and less than %zu\n", pr_info("Last test to run has to be non-negative and less than %zu\n",
NTESTS); NTESTS);
@ -907,14 +907,14 @@ static bool parse_args(int argc, char *argv[],
} }
break; break;
case 'l': case 'l':
targs->seconds = atoi(optarg); targs->seconds = atoi_paranoid(optarg);
if (targs->seconds < 0) { if (targs->seconds < 0) {
pr_info("Test length in seconds has to be non-negative\n"); pr_info("Test length in seconds has to be non-negative\n");
return false; return false;
} }
break; break;
case 'r': case 'r':
targs->runs = atoi(optarg); targs->runs = atoi_paranoid(optarg);
if (targs->runs <= 0) { if (targs->runs <= 0) {
pr_info("Runs per test has to be positive\n"); pr_info("Runs per test has to be positive\n");
return false; return false;

View File

@ -407,7 +407,7 @@ int main(int argc, char *argv[])
#ifdef __x86_64__ #ifdef __x86_64__
if (argc > 1) if (argc > 1)
loops = atoi(argv[1]); loops = atoi_paranoid(argv[1]);
else else
loops = 10; loops = 10;

View File

@ -241,10 +241,10 @@ int main(int argc, char **argv)
while ((opt = getopt(argc, argv, "hp:t:r")) != -1) { while ((opt = getopt(argc, argv, "hp:t:r")) != -1) {
switch (opt) { switch (opt) {
case 'p': case 'p':
reclaim_period_ms = atoi(optarg); reclaim_period_ms = atoi_paranoid(optarg);
break; break;
case 't': case 't':
token = atoi(optarg); token = atoi_paranoid(optarg);
break; break;
case 'r': case 'r':
reboot_permissions = true; reboot_permissions = true;