ranger: Verify gimple_call_num_args for several builtins [PR123431]

Message ID aWIOgaLXO8Vi45Ou@tucnak
State New
Headers
Series ranger: Verify gimple_call_num_args for several builtins [PR123431] |

Commit Message

Jakub Jelinek Jan. 10, 2026, 8:32 a.m. UTC
  Hi!

While gimple_call_combined_fn already do call
gimple_builtin_call_types_compatible_p and for most of builtins ensures
the right types of arguments, for type generic builtins it does not,
from POV of that function those functions are rettype (...).
Now, while the FE does some number of argument checking for the type
generic builtins, as the testcase below shows, it can be gamed.

So, this patch checks the number of arguments for type generic builtins
and does nothing if they have unexpected number of arguments.
Also for the returns arg verifies it can access the first argument.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2026-01-10  Jakub Jelinek  <jakub@redhat.com>

	PR tree-optimization/123431
	* gimple-range-op.cc (gimple_range_op_handler::maybe_builtin_call):
	Punt if type-generic builtins with a single argument don't have
	exactly one argument.  For returns_arg punt if call doesn't have
	at least one argument.

	* gcc.dg/pr123431.c: New test.


	Jakub
  

Comments

Richard Biener Jan. 10, 2026, 10:04 a.m. UTC | #1
> Am 10.01.2026 um 09:33 schrieb Jakub Jelinek <jakub@redhat.com>:
> 
> Hi!
> 
> While gimple_call_combined_fn already do call
> gimple_builtin_call_types_compatible_p and for most of builtins ensures
> the right types of arguments, for type generic builtins it does not,
> from POV of that function those functions are rettype (...).
> Now, while the FE does some number of argument checking for the type
> generic builtins, as the testcase below shows, it can be gamed.
> 
> So, this patch checks the number of arguments for type generic builtins
> and does nothing if they have unexpected number of arguments.
> Also for the returns arg verifies it can access the first argument.
> 
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

LGTM 

Richard 

> 2026-01-10  Jakub Jelinek  <jakub@redhat.com>
> 
>    PR tree-optimization/123431
>    * gimple-range-op.cc (gimple_range_op_handler::maybe_builtin_call):
>    Punt if type-generic builtins with a single argument don't have
>    exactly one argument.  For returns_arg punt if call doesn't have
>    at least one argument.
> 
>    * gcc.dg/pr123431.c: New test.
> 
> --- gcc/gimple-range-op.cc.jj    2026-01-02 09:56:10.182336262 +0100
> +++ gcc/gimple-range-op.cc    2026-01-09 15:00:01.166808500 +0100
> @@ -1410,6 +1410,8 @@ gimple_range_op_handler::maybe_builtin_c
>   switch (func)
>     {
>     case CFN_BUILT_IN_CONSTANT_P:
> +      if (gimple_call_num_args (call) != 1)
> +    return;
>       m_op1 = gimple_call_arg (call, 0);
>       if (irange::supports_p (TREE_TYPE (m_op1)))
>    m_operator = &op_cfn_constant_p;
> @@ -1420,21 +1422,29 @@ gimple_range_op_handler::maybe_builtin_c
>       break;
> 
>     CASE_FLT_FN (CFN_BUILT_IN_SIGNBIT):
> +      if (gimple_call_num_args (call) != 1)
> +    return;
>       m_op1 = gimple_call_arg (call, 0);
>       m_operator = &op_cfn_signbit;
>       break;
> 
>     CASE_FLT_FN (CFN_BUILT_IN_ISINF):
> +      if (gimple_call_num_args (call) != 1)
> +    return;
>       m_op1 = gimple_call_arg (call, 0);
>       m_operator = &op_cfn_isinf;
>       break;
> 
>     case CFN_BUILT_IN_ISFINITE:
> +      if (gimple_call_num_args (call) != 1)
> +    return;
>       m_op1 = gimple_call_arg (call, 0);
>       m_operator = &op_cfn_isfinite;
>       break;
> 
>     case CFN_BUILT_IN_ISNORMAL:
> +      if (gimple_call_num_args (call) != 1)
> +    return;
>       m_op1 = gimple_call_arg (call, 0);
>       m_operator = &op_cfn_isnormal;
>       break;
> @@ -1565,7 +1575,9 @@ gimple_range_op_handler::maybe_builtin_c
>     default:
>       {
>    unsigned arg;
> -    if (gimple_call_fnspec (call).returns_arg (&arg) && arg == 0)
> +    if (gimple_call_fnspec (call).returns_arg (&arg)
> +        && arg == 0
> +        && gimple_call_num_args (call) > 0)
>      {
>        m_op1 = gimple_call_arg (call, 0);
>        m_operator = &op_cfn_pass_through_arg1;
> --- gcc/testsuite/gcc.dg/pr123431.c.jj    2026-01-09 15:10:58.084406597 +0100
> +++ gcc/testsuite/gcc.dg/pr123431.c    2026-01-09 15:10:20.589057380 +0100
> @@ -0,0 +1,19 @@
> +/* PR tree-optimization/123431 */
> +/* { dg-do compile } */
> +/* { dg-options "-O2" } */
> +
> +extern void foo (int);
> +
> +extern inline __attribute__((always_inline)) void
> +bar (int x, ...)
> +{
> +  if (__builtin_constant_p (__builtin_va_arg_pack ()))
> +    foo (x);
> +}
> +
> +void
> +baz (int x)
> +{
> +  bar (1, 2);
> +  bar (3, x);
> +}
> 
>    Jakub
>
  

Patch

--- gcc/gimple-range-op.cc.jj	2026-01-02 09:56:10.182336262 +0100
+++ gcc/gimple-range-op.cc	2026-01-09 15:00:01.166808500 +0100
@@ -1410,6 +1410,8 @@  gimple_range_op_handler::maybe_builtin_c
   switch (func)
     {
     case CFN_BUILT_IN_CONSTANT_P:
+      if (gimple_call_num_args (call) != 1)
+	return;
       m_op1 = gimple_call_arg (call, 0);
       if (irange::supports_p (TREE_TYPE (m_op1)))
 	m_operator = &op_cfn_constant_p;
@@ -1420,21 +1422,29 @@  gimple_range_op_handler::maybe_builtin_c
       break;
 
     CASE_FLT_FN (CFN_BUILT_IN_SIGNBIT):
+      if (gimple_call_num_args (call) != 1)
+	return;
       m_op1 = gimple_call_arg (call, 0);
       m_operator = &op_cfn_signbit;
       break;
 
     CASE_FLT_FN (CFN_BUILT_IN_ISINF):
+      if (gimple_call_num_args (call) != 1)
+	return;
       m_op1 = gimple_call_arg (call, 0);
       m_operator = &op_cfn_isinf;
       break;
 
     case CFN_BUILT_IN_ISFINITE:
+      if (gimple_call_num_args (call) != 1)
+	return;
       m_op1 = gimple_call_arg (call, 0);
       m_operator = &op_cfn_isfinite;
       break;
 
     case CFN_BUILT_IN_ISNORMAL:
+      if (gimple_call_num_args (call) != 1)
+	return;
       m_op1 = gimple_call_arg (call, 0);
       m_operator = &op_cfn_isnormal;
       break;
@@ -1565,7 +1575,9 @@  gimple_range_op_handler::maybe_builtin_c
     default:
       {
 	unsigned arg;
-	if (gimple_call_fnspec (call).returns_arg (&arg) && arg == 0)
+	if (gimple_call_fnspec (call).returns_arg (&arg)
+	    && arg == 0
+	    && gimple_call_num_args (call) > 0)
 	  {
 	    m_op1 = gimple_call_arg (call, 0);
 	    m_operator = &op_cfn_pass_through_arg1;
--- gcc/testsuite/gcc.dg/pr123431.c.jj	2026-01-09 15:10:58.084406597 +0100
+++ gcc/testsuite/gcc.dg/pr123431.c	2026-01-09 15:10:20.589057380 +0100
@@ -0,0 +1,19 @@ 
+/* PR tree-optimization/123431 */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+extern void foo (int);
+
+extern inline __attribute__((always_inline)) void
+bar (int x, ...)
+{
+  if (__builtin_constant_p (__builtin_va_arg_pack ()))
+    foo (x);
+}
+
+void
+baz (int x)
+{
+  bar (1, 2);
+  bar (3, x);
+}