From: Mounir IDRASSI Date: Thu, 25 Jun 2026 13:09:30 +0000 (+0900) Subject: Remove remaining atoi()/atol() calls X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=833efe31fc163fb6216d6406396168cb87cea403;p=thirdparty%2Fopenssl.git Remove remaining atoi()/atol() calls Remove the remaining direct atoi()/atol() uses across apps, libcrypto, libssl, providers, demos and tests, and route the replacements through checked helpers instead of open-coding the conversion at each call site: - apps use the existing opt_int()/opt_long()/opt_int_arg() parsers; - libcrypto and provider code use the new internal ossl_strtol()/ ossl_strtoint() helpers, a checked (and, for ossl_strtoint(), narrowing) signed counterpart to the public OPENSSL_strtoul(); - libssl, demos and tests use OPENSSL_strtoul(); the test code shares a single test_strtoint() helper rather than repeating the bound check and cast per file; - unsigned values use OPENSSL_strtoul(), with call-site-specific range checks before narrowing where the destination type requires it. Reject conversion failures and narrow through the helpers rather than acting on garbage, and preserve the signed RSA-PSS saltlen sentinels while rejecting invalid numeric strings. Add regression coverage for the invalid RSA-PSS saltlen string path. Fixes #8216 Assisted-by: OpenCode:GLM-5.2 Reviewed-by: Tom Cosgrove Reviewed-by: Tim Hudson Reviewed-by: Tomas Mraz MergeDate: Wed Jul 29 17:02:18 2026 (Merged from https://github.com/openssl/openssl/pull/31731) --- diff --git a/apps/ca.c b/apps/ca.c index 8f98bc9a6a4..c7b5a1c22d5 100644 --- a/apps/ca.c +++ b/apps/ca.c @@ -484,13 +484,16 @@ int ca_main(int argc, char **argv) crl_nextupdate = opt_arg(); break; case OPT_CRLDAYS: - crldays = atol(opt_arg()); + if (!opt_long(opt_arg(), &crldays)) + goto opthelp; break; case OPT_CRLHOURS: - crlhours = atol(opt_arg()); + if (!opt_long(opt_arg(), &crlhours)) + goto opthelp; break; case OPT_CRLSEC: - crlsec = atol(opt_arg()); + if (!opt_long(opt_arg(), &crlsec)) + goto opthelp; break; case OPT_INFILES: req = 1; diff --git a/apps/cmp.c b/apps/cmp.c index abe6de5cb99..e75ed35a482 100644 --- a/apps/cmp.c +++ b/apps/cmp.c @@ -3706,9 +3706,13 @@ static int handle_opts_upfront(int argc, char **argv) opt_section = argv[++i]; else if (strcmp(argv[i] + 1, cmp_options[OPT_VERBOSITY - OPT_HELP].name) - == 0 - && !set_verbosity(atoi(argv[++i]))) - return 0; + == 0) { + int level; + + ++i; + if (!opt_int(argv[i], &level) || !set_verbosity(level)) + return 0; + } } } if (opt_section[0] == '\0') /* empty string */ diff --git a/apps/lib/apps.c b/apps/lib/apps.c index 63a8c8c00db..5d082992ec4 100644 --- a/apps/lib/apps.c +++ b/apps/lib/apps.c @@ -252,10 +252,8 @@ static char *app_get_pass(const char *arg, int keepbio) } else if (CHECK_AND_SKIP_PREFIX(arg, "fd:")) { BIO *btmp; - i = atoi(arg); - if (i >= 0) - pwdbio = BIO_new_fd(i, BIO_NOCLOSE); - if ((i < 0) || pwdbio == NULL) { + if (!opt_int(arg, &i) || i < 0 + || (pwdbio = BIO_new_fd(i, BIO_NOCLOSE)) == NULL) { BIO_printf(bio_err, "Can't access file descriptor %s\n", arg); return NULL; } diff --git a/apps/req.c b/apps/req.c index 83ac38ef864..19933ea3520 100644 --- a/apps/req.c +++ b/apps/req.c @@ -1557,10 +1557,12 @@ static EVP_PKEY_CTX *set_keygen_ctx(const char *gstr, /* Treat the second part of gstr, if there is one */ if (gstr != NULL) { /* If the second part starts with a digit, we assume it's a size */ - if (!expect_paramfile && gstr[0] >= '0' && gstr[0] <= '9') - keylen = atol(gstr); - else + if (!expect_paramfile && gstr[0] >= '0' && gstr[0] <= '9') { + if (!opt_long(gstr, &keylen)) + return NULL; + } else { paramfile = gstr; + } } if (paramfile != NULL) { diff --git a/apps/s_client.c b/apps/s_client.c index 65aff3eb42a..80bb0f5305c 100644 --- a/apps/s_client.c +++ b/apps/s_client.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "internal/nelem.h" #include "internal/sockets.h" /* for openssl_fdset() */ @@ -1520,7 +1521,8 @@ int s_client_main(int argc, char **argv) break; case OPT_MTU: #ifndef OPENSSL_NO_DTLS - socket_mtu = atol(opt_arg()); + if (!opt_long(opt_arg(), &socket_mtu)) + goto opthelp; #endif break; case OPT_FALLBACKSCSV: @@ -1625,7 +1627,20 @@ int s_client_main(int argc, char **argv) len = (int)strlen(p); for (start = 0, i = 0; i <= len; ++i) { if (i == len || p[i] == ',') { - serverinfo_types[serverinfo_count] = atoi(p + start); + char *end; + unsigned long ul; + + /* + * Parse only the current comma-separated component. + * OPENSSL_strtoul() with an endptr consumes the leading + * digits; we require it to stop exactly at the delimiter + * (or NUL) and to fit in an unsigned short. + */ + if (!OPENSSL_strtoul(p + start, &end, 10, &ul) + || end != p + i + || ul > USHRT_MAX) + goto opthelp; + serverinfo_types[serverinfo_count] = (unsigned short)ul; if (++serverinfo_count == MAX_SI_TYPES) break; start = i + 1; diff --git a/apps/s_server.c b/apps/s_server.c index 4d99e5442b2..2fc193980c2 100644 --- a/apps/s_server.c +++ b/apps/s_server.c @@ -1885,7 +1885,7 @@ int s_server_main(int argc, char *argv[]) break; #endif case OPT_NACCEPT: - naccept = atol(opt_arg()); + naccept = opt_int_arg(); break; case OPT_VERIFY: s_server_verify = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; @@ -2254,7 +2254,8 @@ int s_server_main(int argc, char *argv[]) break; case OPT_MTU: #ifndef OPENSSL_NO_DTLS - socket_mtu = atol(opt_arg()); + if (!opt_long(opt_arg(), &socket_mtu)) + goto opthelp; #endif break; case OPT_LISTEN: diff --git a/apps/speed.c b/apps/speed.c index b1732744d3a..569f438a395 100644 --- a/apps/speed.c +++ b/apps/speed.c @@ -3946,11 +3946,15 @@ int speed_main(int argc, char **argv) } if (strncmp(sig_name, "dsa", 3) == 0) { + int dsa_nbits = 0; + + if (!opt_int(sig_name + 3, &dsa_nbits)) + goto sig_err_break; ctx_params = EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL); if (ctx_params == NULL || EVP_PKEY_paramgen_init(ctx_params) <= 0 || EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx_params, - atoi(sig_name + 3)) + dsa_nbits) <= 0 || EVP_PKEY_paramgen(ctx_params, &pkey_params) <= 0 || (sig_gen_ctx = EVP_PKEY_CTX_new(pkey_params, NULL)) == NULL diff --git a/crypto/evp/ctrl_params_translate.c b/crypto/evp/ctrl_params_translate.c index c515c6cbbf9..d0529166d78 100644 --- a/crypto/evp/ctrl_params_translate.c +++ b/crypto/evp/ctrl_params_translate.c @@ -1046,9 +1046,19 @@ static int fix_dh_nid5114(enum state state, case PRE_CTRL_STR_TO_PARAMS: if (ctx->p2 == NULL) return 0; - if ((ctx->p2 = (char *)ossl_ffc_named_group_get_name(ossl_ffc_uid_to_dh_named_group(atoi(ctx->p2)))) == NULL) { - ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_VALUE); - return 0; + { + int uid; + + if (!ossl_strtoint(ctx->p2, NULL, 10, &uid)) { + ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_VALUE); + return 0; + } + ctx->p2 = (char *)ossl_ffc_named_group_get_name( + ossl_ffc_uid_to_dh_named_group(uid)); + if (ctx->p2 == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_VALUE); + return 0; + } } ctx->p1 = 0; @@ -1076,8 +1086,14 @@ static int fix_dh_paramgen_type(enum state state, return 0; if (state == PRE_CTRL_STR_TO_PARAMS) { - if ((ctx->p2 = (char *)ossl_dh_gen_type_id2name(atoi(ctx->p2))) - == NULL) { + int id; + + if (!ossl_strtoint(ctx->p2, NULL, 10, &id)) { + ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_VALUE); + return 0; + } + ctx->p2 = (char *)ossl_dh_gen_type_id2name(id); + if (ctx->p2 == NULL) { ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_VALUE); return 0; } @@ -1433,8 +1449,14 @@ static int fix_rsa_pss_saltlen(enum state state, break; } - val = i == OSSL_NELEM(str_value_map) ? atoi(ctx->p2) - : (int)str_value_map[i].id; + if (i == OSSL_NELEM(str_value_map)) { + if (!ossl_strtoint(ctx->p2, NULL, 10, &val)) { + ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_VALUE); + return 0; + } + } else { + val = (int)str_value_map[i].id; + } if (state == POST_CTRL_TO_PARAMS) { /* * EVP_PKEY_CTRL_GET_RSA_PSS_SALTLEN weirdness explained further diff --git a/crypto/mem.c b/crypto/mem.c index 476d6b25293..408c8edfc0b 100644 --- a/crypto/mem.c +++ b/crypto/mem.c @@ -112,12 +112,19 @@ static void parseit(void) { char *semi = strchr(md_failstring, ';'); char *atsign; + char *end; if (semi != NULL) *semi++ = '\0'; - /* Get the count (atol will stop at the @ if there), and percentage */ - md_count = atol(md_failstring); + /* + * Get the count (parsing stops at the '@' if present), and percentage. + * Ignore an unparsable/overflowing count rather than acting on garbage. + * Validate that the count is followed by '@' or end-of-string. + */ + if (!ossl_strtol(md_failstring, &end, 10, &md_count) + || (*end != '\0' && *end != '@')) + md_count = 0; atsign = strchr(md_failstring, '@'); md_fail_percent = atsign == NULL ? 0 : (int)(atof(atsign + 1) * 100 + 0.5); @@ -179,10 +186,19 @@ void ossl_malloc_setup_failures(void) parseit(); } } - if ((cp = getenv("OPENSSL_MALLOC_FD")) != NULL) - md_tracefd = atoi(cp); - if ((cp = getenv("OPENSSL_MALLOC_SEED")) != NULL) - srandom(atoi(cp)); + if ((cp = getenv("OPENSSL_MALLOC_FD")) != NULL) { + int fd; + + if (ossl_strtoint(cp, NULL, 10, &fd) && fd >= 0) + md_tracefd = fd; + } + if ((cp = getenv("OPENSSL_MALLOC_SEED")) != NULL) { + unsigned long seed; + + /* Any value is a usable seed; just truncate it to the srandom() type. */ + if (OPENSSL_strtoul(cp, NULL, 10, &seed)) + srandom((unsigned int)seed); + } } #endif diff --git a/crypto/o_str.c b/crypto/o_str.c index 2192d48775d..d48b43df17d 100644 --- a/crypto/o_str.c +++ b/crypto/o_str.c @@ -161,6 +161,52 @@ int OPENSSL_strtoul(const char *str, char **endptr, int base, return 1; } +/* + * Internal signed counterpart to OPENSSL_strtoul(). Parses a signed long with + * the same validation rules: it fails on a conversion error (including + * overflow/underflow), if no digits were consumed, or, when |endptr| is NULL, + * if the whole string was not consumed. Returns 1 on success (storing the + * result in |*result|), 0 otherwise. + */ +int ossl_strtol(const char *str, char **endptr, int base, long *result) +{ + char *tmp_endptr; + char **internal_endptr = endptr == NULL ? &tmp_endptr : endptr; + + errno = 0; + + *internal_endptr = (char *)str; + + if (result == NULL || str == NULL) + return 0; + + *result = strtol(str, internal_endptr, base); + if (errno != 0 + || (endptr == NULL && **internal_endptr != '\0') + || str == *internal_endptr) + return 0; + + return 1; +} + +/* + * As ossl_strtol() but stores the result in an int, additionally failing if + * the parsed value does not fit in an int. Returns 1 on success (storing the + * result in |*result|), 0 otherwise. + */ +int ossl_strtoint(const char *str, char **endptr, int base, int *result) +{ + long l; + + if (result == NULL || !ossl_strtol(str, endptr, base, &l)) + return 0; + if (l < INT_MIN || l > INT_MAX) + return 0; + + *result = (int)l; + return 1; +} + int OPENSSL_hexchar2int(unsigned char c) { #ifdef CHARSET_EBCDIC diff --git a/crypto/ppccap.c b/crypto/ppccap.c index e029eb3051a..189c9e03d46 100644 --- a/crypto/ppccap.c +++ b/crypto/ppccap.c @@ -160,11 +160,16 @@ void OPENSSL_cpuid_setup(void) if (sizeof(size_t) == 4) { struct utsname uts; + long major; + char *end; #if defined(_SC_AIX_KERNEL_BITMODE) if (sysconf(_SC_AIX_KERNEL_BITMODE) != 64) return; #endif - if (uname(&uts) != 0 || atoi(uts.version) < 6) + if (uname(&uts) != 0 + || !ossl_strtol(uts.version, &end, 10, &major) + || (*end != '\0' && *end != '.') + || major < 6) return; } diff --git a/crypto/ts/ts_conf.c b/crypto/ts/ts_conf.c index f8d01813df5..1c23eaca896 100644 --- a/crypto/ts/ts_conf.c +++ b/crypto/ts/ts_conf.c @@ -358,14 +358,26 @@ int TS_CONF_set_accuracy(CONF *conf, const char *section, TS_RESP_CTX *ctx) for (i = 0; i < sk_CONF_VALUE_num(list); ++i) { CONF_VALUE *val = sk_CONF_VALUE_value(list, i); if (strcmp(val->name, ENV_VALUE_SECS) == 0) { - if (val->value) - secs = atoi(val->value); + if (val->value != NULL + && (!ossl_strtoint(val->value, NULL, 10, &secs) + || secs < 0)) { + ts_CONF_invalid(section, ENV_ACCURACY); + goto err; + } } else if (strcmp(val->name, ENV_VALUE_MILLISECS) == 0) { - if (val->value) - millis = atoi(val->value); + if (val->value != NULL + && (!ossl_strtoint(val->value, NULL, 10, &millis) + || millis < 0)) { + ts_CONF_invalid(section, ENV_ACCURACY); + goto err; + } } else if (strcmp(val->name, ENV_VALUE_MICROSECS) == 0) { - if (val->value) - micros = atoi(val->value); + if (val->value != NULL + && (!ossl_strtoint(val->value, NULL, 10, µs) + || micros < 0)) { + ts_CONF_invalid(section, ENV_ACCURACY); + goto err; + } } else { ts_CONF_invalid(section, ENV_ACCURACY); goto err; diff --git a/demos/digest/EVP_MD_xof.c b/demos/digest/EVP_MD_xof.c index f0c9de3e5fd..d31bd82d128 100644 --- a/demos/digest/EVP_MD_xof.c +++ b/demos/digest/EVP_MD_xof.c @@ -9,8 +9,10 @@ #include #include +#include #include #include +#include #include /* @@ -53,7 +55,13 @@ int main(int argc, char **argv) /* Allow digest length to be changed for demonstration purposes. */ if (argc > 1) { - digest_len_i = atoi(argv[1]); + unsigned long ul; + + if (!OPENSSL_strtoul(argv[1], NULL, 10, &ul) || ul > INT_MAX) { + fprintf(stderr, "Specify a non-negative digest length\n"); + goto end; + } + digest_len_i = (int)ul; if (digest_len_i <= 0) { fprintf(stderr, "Specify a non-negative digest length\n"); goto end; diff --git a/demos/pkey/EVP_PKEY_RSA_keygen.c b/demos/pkey/EVP_PKEY_RSA_keygen.c index a889ab6f77d..343bc5f296b 100644 --- a/demos/pkey/EVP_PKEY_RSA_keygen.c +++ b/demos/pkey/EVP_PKEY_RSA_keygen.c @@ -17,8 +17,10 @@ #include #include +#include #include #include +#include #include #include #include @@ -253,7 +255,13 @@ int main(int argc, char **argv) } if (argc > 1) { - bits_i = atoi(argv[1]); + unsigned long ul; + + if (!OPENSSL_strtoul(argv[1], NULL, 10, &ul) || ul > INT_MAX) { + fprintf(stderr, "Invalid RSA key size\n"); + return EXIT_FAILURE; + } + bits_i = (int)ul; if (bits_i < 512) { fprintf(stderr, "Invalid RSA key size\n"); return EXIT_FAILURE; diff --git a/doc/man3/OPENSSL_malloc.pod b/doc/man3/OPENSSL_malloc.pod index 1907469fcd1..a22099ac3e1 100644 --- a/doc/man3/OPENSSL_malloc.pod +++ b/doc/man3/OPENSSL_malloc.pod @@ -47,7 +47,7 @@ OPENSSL_MALLOC_SEED char *OPENSSL_strndup(const char *str, size_t s); size_t OPENSSL_strlcat(char *dst, const char *src, size_t size); size_t OPENSSL_strlcpy(char *dst, const char *src, size_t size); - int OPENSSL_strtoul(char *src, char **endptr, int base, unsigned long *num); + int OPENSSL_strtoul(const char *str, char **endptr, int base, unsigned long *num); void *OPENSSL_memdup(void *data, size_t s); void *OPENSSL_clear_realloc(void *p, size_t old_len, size_t num); void *OPENSSL_clear_realloc_array(void *p, size_t old_len, size_t num, @@ -226,8 +226,8 @@ work on all platforms): ...app invocation... 3>/tmp/log$$ If the environment variable B is set, its value -is interpreted as an integer using atoi(3) and supplied to the srandom(3) -call for the random number generator initialisation. +is parsed using OPENSSL_strtoul() and supplied to the srandom(3) call for +the random number generator initialisation. =head1 RETURN VALUES diff --git a/include/internal/cryptlib.h b/include/internal/cryptlib.h index 0dcc6b6acd3..8bc22e39ce3 100644 --- a/include/internal/cryptlib.h +++ b/include/internal/cryptlib.h @@ -162,6 +162,19 @@ char *ossl_buf2hexstr_sep(const unsigned char *buf, long buflen, char sep); unsigned char *ossl_hexstr2buf_sep(const char *str, long *buflen, const char sep); +/* + * Parse a signed long with validation; see OPENSSL_strtoul() for the rules. + * Returns 1 on success (and stores the result in |*result|), 0 on failure. + */ +int ossl_strtol(const char *str, char **endptr, int base, long *result); + +/* + * As ossl_strtol() but stores the result in an int, additionally failing if + * the parsed value does not fit in an int. + * Returns 1 on success (and stores the result in |*result|), 0 on failure. + */ +int ossl_strtoint(const char *str, char **endptr, int base, int *result); + /** * Writes |n| value in hex format into |buf|, * and returns the number of bytes written diff --git a/providers/implementations/rands/seeding/rand_unix.c b/providers/implementations/rands/seeding/rand_unix.c index 67b38cb7199..d1bda5c68e6 100644 --- a/providers/implementations/rands/seeding/rand_unix.c +++ b/providers/implementations/rands/seeding/rand_unix.c @@ -451,9 +451,19 @@ static int wait_random_seeded(void) * this alternative but essentially identical source moot. */ if (uname(&un) == 0) { - kernel[0] = atoi(un.release); + int val; + char *end; + + kernel[0] = (ossl_strtoint(un.release, &end, 10, &val) + && (*end == '.' || *end == '\0')) + ? val + : 0; p = strchr(un.release, '.'); - kernel[1] = p == NULL ? 0 : atoi(p + 1); + kernel[1] = (p != NULL + && ossl_strtoint(p + 1, &end, 10, &val) + && (*end == '.' || *end == '\0')) + ? val + : 0; if (kernel[0] > kernel_version[0] || (kernel[0] == kernel_version[0] && kernel[1] >= kernel_version[1])) { diff --git a/providers/implementations/signature/rsa_sig.c b/providers/implementations/signature/rsa_sig.c index 232414935f3..7b35fec0282 100644 --- a/providers/implementations/signature/rsa_sig.c +++ b/providers/implementations/signature/rsa_sig.c @@ -1695,8 +1695,11 @@ static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[]) saltlen = RSA_PSS_SALTLEN_AUTO; else if (strcmp(p.slen->data, OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO_DIGEST_MAX) == 0) saltlen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX; - else - saltlen = atoi(p.slen->data); + else if (!ossl_strtoint(p.slen->data, NULL, 10, &saltlen)) { + ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH, + "invalid RSA-PSS saltlen value"); + return 0; + } } /* diff --git a/ssl/ssl_conf.c b/ssl/ssl_conf.c index a7293512b9b..d065b649d5a 100644 --- a/ssl/ssl_conf.c +++ b/ssl/ssl_conf.c @@ -9,6 +9,7 @@ #include "internal/e_os.h" +#include #include #include "ssl_local.h" #include @@ -723,9 +724,11 @@ out: static int cmd_NumTickets(SSL_CONF_CTX *cctx, const char *value) { int rv = 0; - int num_tickets = atoi(value); + unsigned long ul; + + if (OPENSSL_strtoul(value, NULL, 10, &ul) && ul <= INT_MAX) { + int num_tickets = (int)ul; - if (num_tickets >= 0) { if (cctx->ctx) rv = SSL_CTX_set_num_tickets(cctx->ctx, num_tickets); if (cctx->ssl) diff --git a/test/evp_test.c b/test/evp_test.c index 77029f40db9..ef6dea96643 100644 --- a/test/evp_test.c +++ b/test/evp_test.c @@ -475,10 +475,9 @@ static int evp_test_buffer_ncopy(const char *value, EVP_TEST_BUFFER *db; unsigned char *tbuf, *p; size_t tbuflen; - int ncopy = atoi(value); - int i; + int ncopy = 0, i; - if (ncopy <= 0) + if (!test_strtoint(value, &ncopy) || ncopy <= 0) return 0; if (sk == NULL || sk_EVP_TEST_BUFFER_num(sk) == 0) return 0; @@ -501,9 +500,9 @@ static int evp_test_buffer_set_count(const char *value, STACK_OF(EVP_TEST_BUFFER) *sk) { EVP_TEST_BUFFER *db; - int count = atoi(value); + int count = 0; - if (count <= 0) + if (!test_strtoint(value, &count) || count <= 0) return 0; if (sk == NULL || sk_EVP_TEST_BUFFER_num(sk) == 0) @@ -764,14 +763,13 @@ static int digest_test_parse(EVP_TEST *t, if (strcmp(keyword, "Ncopy") == 0) return evp_test_buffer_ncopy(value, mdata->input); if (strcmp(keyword, "Padding") == 0) - return (mdata->pad_type = atoi(value)) > 0; + return test_strtoint(value, &mdata->pad_type) && mdata->pad_type > 0; if (strcmp(keyword, "XOF") == 0) - return (mdata->xof = atoi(value)) > 0; + return test_strtoint(value, &mdata->xof) && mdata->xof > 0; if (strcmp(keyword, "OutputSize") == 0) { int sz; - sz = atoi(value); - if (sz < 0) + if (!test_strtoint(value, &sz)) return -1; mdata->digest_size = sz; return 1; @@ -1065,8 +1063,7 @@ static int cipher_test_parse(EVP_TEST *t, const char *keyword, if (strcmp(keyword, "Key") == 0) return parse_bin(value, &cdat->key, &cdat->key_len); if (strcmp(keyword, "Rounds") == 0) { - i = atoi(value); - if (i < 0) + if (!test_strtoint(value, &i)) return -1; cdat->rounds = (unsigned int)i; return 1; @@ -1080,8 +1077,7 @@ static int cipher_test_parse(EVP_TEST *t, const char *keyword, if (strcmp(keyword, "Ciphertext") == 0) return parse_bin(value, &cdat->ciphertext, &cdat->ciphertext_len); if (strcmp(keyword, "KeyBits") == 0) { - i = atoi(value); - if (i < 0) + if (!test_strtoint(value, &i)) return -1; cdat->key_bits = (size_t)i; return 1; @@ -1838,14 +1834,12 @@ static int mac_test_parse(EVP_TEST *t, if (strcmp(keyword, "Ctrl") == 0) return ctrladd(mdata->controls, value); if (strcmp(keyword, "OutputSize") == 0) { - mdata->output_size = atoi(value); - if (mdata->output_size < 0) + if (!test_strtoint(value, &mdata->output_size)) return -1; return 1; } if (strcmp(keyword, "BlockSize") == 0) { - mdata->block_size = atoi(value); - if (mdata->block_size < 0) + if (!test_strtoint(value, &mdata->block_size)) return -1; return 1; } @@ -3451,8 +3445,7 @@ static int pbkdf2_test_parse(EVP_TEST *t, PBE_DATA *pdata = t->data; if (strcmp(keyword, "iter") == 0) { - pdata->iter = atoi(value); - if (pdata->iter <= 0) + if (!test_strtoint(value, &pdata->iter) || pdata->iter <= 0) return -1; return 1; } @@ -3471,8 +3464,7 @@ static int pkcs12_test_parse(EVP_TEST *t, PBE_DATA *pdata = t->data; if (strcmp(keyword, "id") == 0) { - pdata->id = atoi(value); - if (pdata->id <= 0) + if (!test_strtoint(value, &pdata->id) || pdata->id <= 0) return -1; return 1; } @@ -3901,7 +3893,8 @@ static int rand_test_parse(EVP_TEST *t, int n; if ((p = strchr(keyword, '.')) != NULL) { - n = atoi(++p); + if (!test_strtoint(++p, &n)) + return 0; if (n >= MAX_RAND_REPEATS) return 0; if (n > rdata->n) @@ -3935,17 +3928,21 @@ static int rand_test_parse(EVP_TEST *t, if (strcmp(keyword, "Digest") == 0) return TEST_ptr(rdata->digest = OPENSSL_strdup(value)); if (strcmp(keyword, "DerivationFunction") == 0) { - rdata->use_df = atoi(value) != 0; + n = 0; + (void)test_strtoint(value, &n); + rdata->use_df = n != 0; return 1; } if (strcmp(keyword, "GenerateBits") == 0) { - if ((n = atoi(value)) <= 0 || n % 8 != 0) + if (!test_strtoint(value, &n) || n <= 0 || n % 8 != 0) return 0; rdata->generate_bits = (unsigned int)n; return 1; } if (strcmp(keyword, "PredictionResistance") == 0) { - rdata->prediction_resistance = atoi(value) != 0; + n = 0; + (void)test_strtoint(value, &n); + rdata->prediction_resistance = n != 0; return 1; } if (strcmp(keyword, "CtrlInit") == 0) @@ -5612,7 +5609,10 @@ start: } t->reason = take_value(pp); } else if (strcmp(pp->key, "Threads") == 0) { - if (OSSL_set_max_threads(libctx, atoi(pp->value)) == 0) { + int nthreads = 0; + + (void)test_strtoint(pp->value, &nthreads); + if (OSSL_set_max_threads(libctx, nthreads) == 0) { TEST_info("skipping, '%s' threads not available: %s:%d", pp->value, t->s.test_file, t->s.start); t->skip = 1; @@ -5630,8 +5630,9 @@ start: TEST_info("Line %d: multiple security category lines", t->s.curr); return 0; } - t->security_category = atoi(pp->value); - if (t->security_category < 0 || t->security_category > 5) { + t->security_category = 0; + if (!test_strtoint(pp->value, &t->security_category) + || t->security_category > 5) { TEST_info("Line %d: invalid security category, should be 0..5", t->s.curr); return 0; diff --git a/test/helpers/ssl_test_ctx.c b/test/helpers/ssl_test_ctx.c index 321d1a378a9..f8b88df8612 100644 --- a/test/helpers/ssl_test_ctx.c +++ b/test/helpers/ssl_test_ctx.c @@ -56,7 +56,8 @@ static int parse_boolean(const char *value, int *result) #define IMPLEMENT_SSL_TEST_INT_OPTION(struct_type, name, field) \ static int parse_##name##_##field(struct_type *ctx, const char *value) \ { \ - ctx->field = atoi(value); \ + if (!test_strtoint(value, &ctx->field)) \ + ctx->field = 0; \ return 1; \ } diff --git a/test/lhash_test.c b/test/lhash_test.c index 87dc71f829a..394321151f3 100644 --- a/test/lhash_test.c +++ b/test/lhash_test.c @@ -590,14 +590,16 @@ static int setup_num_workers(void) char *lhash_workers = getenv("LHASH_WORKERS"); /* If we have HARNESS_JOBS set, don't eat more than a quarter */ if (harness_jobs != NULL) { - int jobs = atoi(harness_jobs); - if (jobs > 0) + int jobs; + + if (test_strtoint(harness_jobs, &jobs) && jobs > 0) num_workers = jobs / 4; } /* But if we have explicitly set LHASH_WORKERS use that */ if (lhash_workers != NULL) { - int jobs = atoi(lhash_workers); - if (jobs > 0) + int jobs; + + if (test_strtoint(lhash_workers, &jobs) && jobs > 0) num_workers = jobs; } diff --git a/test/mfail/mfail.c b/test/mfail/mfail.c index ec391c8e85e..70f575afc92 100644 --- a/test/mfail/mfail.c +++ b/test/mfail/mfail.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #if defined(__has_feature) @@ -69,7 +70,12 @@ static int env_is_true(const char *name) static int env_int(const char *name, int dflt) { const char *value = getenv(name); - return (value != NULL && *value != '\0') ? atoi(value) : dflt; + unsigned long ul; + + if (value != NULL && *value != '\0' + && OPENSSL_strtoul(value, NULL, 10, &ul) && ul <= INT_MAX) + return (int)ul; + return dflt; } static void mfail_print_bt(void) diff --git a/test/recipes/30-test_evp_data/evppkey_rsa_common.txt b/test/recipes/30-test_evp_data/evppkey_rsa_common.txt index e97ff64c095..58ac329afec 100644 --- a/test/recipes/30-test_evp_data/evppkey_rsa_common.txt +++ b/test/recipes/30-test_evp_data/evppkey_rsa_common.txt @@ -966,6 +966,12 @@ Input="0123456789ABCDEF0123456789ABCDEF" Output=4DE433D5844043EF08D354DA03CB29068780D52706D7D1E4D50EFB7D58C9D547D83A747DDD0635A96B28F854E50145518482CB49E963054621B53C60C498D07C16E9C2789C893CF38D4D86900DE71BDE463BD2761D1271E358C7480A1AC0BAB930DDF39602AD1BC165B5D7436B516B7A7858E8EB7AB1C420EEB482F4D207F0E462B1724959320A084E13848D11D10FB593E66BF680BF6D3F345FC3E9C3DE60ABBAC37E1C6EC80A268C8D9FC49626C679097AA690BC1AA662B95EB8DB70390861AA0898229F9349B4B5FDD030D4928C47084708A933144BE23BD3C6E661B85B2C0EF9ED36D498D5B7320E8194D363D4AD478C059BAE804181965E0B81B663158A Result = VERIFY_ERROR +# Invalid numeric salt length string +Verify = RSA-2048-PUBLIC +Ctrl = rsa_padding_mode:pss +Ctrl = rsa_pss_saltlen:1x +Result = PKEY_CTRL_ERROR + # Verify using default parameters, explicitly setting parameters Verify = RSA-PSS-DEFAULT Ctrl = rsa_padding_mode:pss diff --git a/test/ssl_old_test.c b/test/ssl_old_test.c index 40df6536e35..48e3f61cf33 100644 --- a/test/ssl_old_test.c +++ b/test/ssl_old_test.c @@ -1043,13 +1043,16 @@ int main(int argc, char *argv[]) } else if (HAS_PREFIX(*argv, "-num")) { if (--argc < 1) goto bad; - number = atoi(*(++argv)); + (void)test_strtoint(*(++argv), &number); if (number == 0) number = 1; } else if (strcmp(*argv, "-bytes") == 0) { + unsigned long ul = 0; + if (--argc < 1) goto bad; - bytes = atol(*(++argv)); + if (OPENSSL_strtoul(*(++argv), NULL, 10, &ul) && ul <= LONG_MAX) + bytes = (long)ul; if (bytes == 0L) bytes = 1L; i = (int)strlen(argv[0]); @@ -1191,9 +1194,12 @@ int main(int argc, char *argv[]) goto bad; client_sess_in = *(++argv); } else if (strcmp(*argv, "-should_reuse") == 0) { + int reuse_arg = 0; + if (--argc < 1) goto bad; - should_reuse = !!atoi(*(++argv)); + (void)test_strtoint(*(++argv), &reuse_arg); + should_reuse = reuse_arg != 0; } else if (strcmp(*argv, "-no_ticket") == 0) { no_ticket = 1; } else if (strcmp(*argv, "-client_ktls") == 0) { diff --git a/test/testutil.h b/test/testutil.h index 7e23f538278..cda7716cdf4 100644 --- a/test/testutil.h +++ b/test/testutil.h @@ -712,6 +712,14 @@ STACK_OF(X509) *load_certs_pem(const char *file); X509_REQ *load_csr_der(const char *file, OSSL_LIB_CTX *libctx); int test_asn1_string_to_time_t(const char *asn1_string, time_t *out_time_t); int compare_with_reference_file(BIO *membio, const char *reffile); + +/* + * Parse a non-negative decimal integer from |value| into |*result|. Returns 1 + * on success, 0 on failure (NULL/empty/non-numeric/negative/overflowing input, + * or trailing garbage). Test binaries link only against the public ABI, so + * this wraps OPENSSL_strtoul() rather than the libcrypto-internal helpers. + */ +int test_strtoint(const char *value, int *result); /* * Create an X509 from an array of strings. */ diff --git a/test/testutil/driver.c b/test/testutil/driver.c index bfcb65b7bc6..8e3637acfeb 100644 --- a/test/testutil/driver.c +++ b/test/testutil/driver.c @@ -13,6 +13,7 @@ #include #include +#include #include "internal/nelem.h" #include @@ -138,14 +139,22 @@ int setup_test_framework(int argc, char *argv[]) char *test_rand_seed = getenv("OPENSSL_TEST_RAND_SEED"); char *TAP_levels = getenv("HARNESS_OSSL_LEVEL"); - if (TAP_levels != NULL) - level = 4 * atoi(TAP_levels); + if (TAP_levels != NULL) { + int n; + + if (test_strtoint(TAP_levels, &n) && n <= INT_MAX / 4) + level = 4 * n; + } test_adjust_streams_tap_level(level); if (test_rand_order != NULL) { + int n; + rand_order = 1; - set_seed(atoi(test_rand_order)); + set_seed(test_strtoint(test_rand_order, &n) ? n : 0); } else if (test_rand_seed != NULL) { - set_seed(atoi(test_rand_seed)); + int n; + + set_seed(test_strtoint(test_rand_seed, &n) ? n : 0); } else { set_seed(0); } @@ -178,8 +187,12 @@ static int check_single_test_params(char *name, char *testname, char *itname) break; } } - if (i >= num_tests) - single_test = atoi(name); + if (i >= num_tests) { + int n; + + if (test_strtoint(name, &n)) + single_test = n; + } } /* if only iteration is specified, assume we want the first test */ diff --git a/test/testutil/helper.c b/test/testutil/helper.c index 551b9a200bc..a23e8c2f230 100644 --- a/test/testutil/helper.c +++ b/test/testutil/helper.c @@ -7,12 +7,24 @@ * https://www.openssl.org/source/license.html */ #include +#include #include #include +#include #include "../testutil.h" +int test_strtoint(const char *value, int *result) +{ + unsigned long ul; + + if (!OPENSSL_strtoul(value, NULL, 10, &ul) || ul > INT_MAX) + return 0; + *result = (int)ul; + return 1; +} + int test_asn1_string_to_time_t(const char *asn1_string, time_t *out_time_t) { int ret = 0; diff --git a/test/timing_load_creds.c b/test/timing_load_creds.c index 5e9e2bf6c0a..45cdc2e1da8 100644 --- a/test/timing_load_creds.c +++ b/test/timing_load_creds.c @@ -9,8 +9,10 @@ #include #include +#include #include +#include #ifdef OPENSSL_SYS_UNIX #include @@ -120,10 +122,14 @@ int main(int ac, char **av) default: usage(); break; - case 'c': - if ((count = atoi(optarg)) < 0) + case 'c': { + unsigned long ul; + + if (!OPENSSL_strtoul(optarg, NULL, 10, &ul) || ul > INT_MAX) usage(); + count = (int)ul; break; + } case 'd': debug = 1; break; diff --git a/util/platform_symbols/unix-symbols.txt b/util/platform_symbols/unix-symbols.txt index 62720f35fd3..bfd60a51b7e 100644 --- a/util/platform_symbols/unix-symbols.txt +++ b/util/platform_symbols/unix-symbols.txt @@ -1,7 +1,6 @@ abort accept aligned_alloc -atoi bcmp bind calloc diff --git a/util/platform_symbols/windows-symbols.txt b/util/platform_symbols/windows-symbols.txt index 7ae58a3f182..cd41c7ec028 100644 --- a/util/platform_symbols/windows-symbols.txt +++ b/util/platform_symbols/windows-symbols.txt @@ -125,7 +125,6 @@ _stat64i32 _strdup _time64 _wfopen -atoi calloc clearerr fclose