From a0a1776bb902e0586816a71a7d7e8f24d63e7f0f Mon Sep 17 00:00:00 2001 From: Virginia Kodsy Date: Wed, 25 Mar 2026 17:39:39 +0200 Subject: [PATCH 23/98] analyzer: add known_function handler for strcasecmp This patch adds a known_function handler for strcasecmp to the static analyzer. It ensures the analyzer checks for null-terminated string arguments and, when a return value is expected (LHS), it conjures a symbolic value for the result. gcc/analyzer/ChangeLog: * kf.cc (class kf_strcasecmp): New. (kf_strcasecmp::impl_call_post): New. (register_known_functions): Register BUILT_IN_STRCASECMP, "strcasecmp", and "__builtin_strcasecmp". gcc/testsuite/ChangeLog: * gcc.dg/analyzer/strcasecmp-1.c: New test. Signed-off-by: Virginia Kodsy --- gcc/analyzer/kf.cc | 43 ++++++++++++++++++++ gcc/testsuite/gcc.dg/analyzer/strcasecmp-1.c | 43 ++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/analyzer/strcasecmp-1.c diff --git a/gcc/analyzer/kf.cc b/gcc/analyzer/kf.cc index 5d2af3866a79..d2605e962529 100644 --- a/gcc/analyzer/kf.cc +++ b/gcc/analyzer/kf.cc @@ -2848,6 +2848,45 @@ region_model::impl_deallocation_call (const call_details &cd) kf.impl_call_post (cd); } +/* Handler for "strcasecmp" */ + +class kf_strcasecmp : public builtin_known_function +{ +public: + bool + matches_call_types_p (const call_details &cd) const final override + { + return (cd.num_args () == 2 + && cd.arg_is_pointer_p (0) + && cd.arg_is_pointer_p (1)); + } + void + impl_call_pre (const call_details &cd) const final override + { + cd.check_for_null_terminated_string_arg (0); + cd.check_for_null_terminated_string_arg (1); + } + + enum built_in_function + builtin_code () const final override + { + return BUILT_IN_STRCASECMP; + } + + void impl_call_post (const call_details &cd) const final override; +}; + +void +kf_strcasecmp::impl_call_post (const call_details &cd) const +{ + if (tree lhs_type = cd.get_lhs_type ()) + { + const svalue *result_val + = cd.get_or_create_conjured_svalue (cd.get_lhs_region ()); + cd.maybe_set_lhs (result_val); + } +} + static void register_atomic_builtins (known_function_manager &kfm) { @@ -3037,6 +3076,8 @@ register_known_functions (known_function_manager &kfm, kfm.add (BUILT_IN_EH_POINTER, std::make_unique ()); + kfm.add(BUILT_IN_STRCASECMP, std::make_unique()); + register_atomic_builtins (kfm); register_sanitizer_builtins (kfm); register_varargs_builtins (kfm); @@ -3101,6 +3142,8 @@ register_known_functions (known_function_manager &kfm, kfm.add ("__builtin_strlen", std::make_unique ()); kfm.add ("strstr", std::make_unique ()); kfm.add ("__builtin_strstr", std::make_unique ()); + kfm.add("strcasecmp", std::make_unique()); + kfm.add("__builtin_strcasecmp", std::make_unique()); } /* Known POSIX functions, and some non-standard extensions. */ diff --git a/gcc/testsuite/gcc.dg/analyzer/strcasecmp-1.c b/gcc/testsuite/gcc.dg/analyzer/strcasecmp-1.c new file mode 100644 index 000000000000..f56bc9df5ddb --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/strcasecmp-1.c @@ -0,0 +1,43 @@ +/* { dg-additional-options "-Wno-nonnull" } */ +#include "analyzer-decls.h" + + +extern int strcasecmp (const char *s1, const char *s2); + +int +test_passthrough (const char *s1, const char *s2) +{ + return strcasecmp (s1, s2); +} + +void +test_literals (void) +{ + strcasecmp ("string", "STRING"); +} + +void +test_unterminated (void) +{ + char buf[3] = {'a', 'b', 'c' }; + strcasecmp (buf, "abc"); /* { dg-warning "stack-based buffer over-read" } */ +} + +void +test_unterminated_2 (void) +{ + char buf[3] = { 'a', 'b', 'c' }; + strcasecmp ("abc", buf); /* { dg-warning "stack-based buffer over-read" } */ +} + +void +test_null_1 (void) +{ + strcasecmp (NULL, "abc"); /* { dg-warning "use of NULL where non-null expected" } */ +} + +void +test_null_2 (void) +{ + strcasecmp("abc", NULL); /* { dg-warning "use of NULL where non-null expected" } */ +} -- 2.49.0