CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
275 lines
11 KiB
C
275 lines
11 KiB
C
// RUN: %clang_analyze_cc1 -triple hexagon-unknown-linux -verify %s \
|
|
// RUN: -analyzer-checker=core,optin.core.UnconditionalVAArg \
|
|
// RUN: -analyzer-output=text
|
|
//
|
|
// RUN: %clang_analyze_cc1 -triple x86_64-pc-linux-gnu -verify %s \
|
|
// RUN: -analyzer-checker=core,optin.core.UnconditionalVAArg \
|
|
// RUN: -analyzer-output=text
|
|
|
|
#include "Inputs/system-header-simulator-for-valist.h"
|
|
int printf(const char *format, ...);
|
|
void abort(void);
|
|
|
|
void log_message(const char *msg, ...) {
|
|
// This artifical but plausible example function expects that the list of
|
|
// variadic arguments is terminated by a NULL pointer. Although careful use
|
|
// of this function is well-defined, passing no variadic arguments would
|
|
// trigger undefined behavior, so it may be better to choose a different way
|
|
// to mark the count of variadic arguments.
|
|
va_list va;
|
|
const char *arg;
|
|
printf("%s\n", msg);
|
|
va_start(va, msg);
|
|
while ((arg = va_arg(va, const char *))) {
|
|
// expected-warning@-1 {{Unconditional use of va_arg()}}
|
|
// expected-note@-2 {{Calls to 'log_message' always reach this va_arg() expression, so calling 'log_message' with no variadic arguments would be undefined behavior}}
|
|
printf(" * %s\n", arg);
|
|
}
|
|
va_end(va);
|
|
}
|
|
|
|
|
|
void never_called(int fst, ...) {
|
|
// This simple test function is not called in this test file.
|
|
va_list va;
|
|
va_start(va, fst);
|
|
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
|
|
// expected-note@-1 {{Calls to 'never_called' always reach this va_arg() expression, so calling 'never_called' with no variadic arguments would be undefined behavior}}
|
|
va_end(va);
|
|
}
|
|
|
|
|
|
void called_with_varargs(int fst, ...) {
|
|
// This function is identical to never_called(), but it is called by another
|
|
// function (which will serve as the entrypoint) and the call passes several
|
|
// variadic arguments. The diagnostic is still raised.
|
|
va_list va;
|
|
va_start(va, fst);
|
|
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
|
|
// expected-note@-1 {{Calls to 'called_with_varargs' always reach this va_arg() expression, so calling 'called_with_varargs' with no variadic arguments would be undefined behavior}}
|
|
va_end(va);
|
|
}
|
|
|
|
void caller1(void) {
|
|
called_with_varargs(1, 2, 3, 4);
|
|
}
|
|
|
|
|
|
void called_without_varargs(int fst, ...) {
|
|
// This function is identical to never_called(), but it is called by another
|
|
// function (which will serve as the entrypoint) and the call passes no
|
|
// variadic arguments. The diagnostic is still raised.
|
|
va_list va;
|
|
va_start(va, fst);
|
|
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
|
|
// expected-note@-1 {{Calls to 'called_without_varargs' always reach this va_arg() expression, so calling 'called_without_varargs' with no variadic arguments would be undefined behavior}}
|
|
// FIXME: Here the checker should mention that this function is _actually_
|
|
// called with no variadic arguments.
|
|
va_end(va);
|
|
}
|
|
|
|
void caller2(void) {
|
|
called_without_varargs(1);
|
|
}
|
|
|
|
|
|
void multiple_va_arg_calls(int fst, ...) {
|
|
// This function function unconditionally calls va_arg() multiple times. To
|
|
// avoid spamming the user, only the first use is reported.
|
|
va_list va;
|
|
va_start(va, fst);
|
|
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
|
|
// expected-note@-1 {{Calls to 'multiple_va_arg_calls' always reach this va_arg() expression, so calling 'multiple_va_arg_calls' with no variadic arguments would be undefined behavior}}
|
|
(void)va_arg(va, int); // no-warning
|
|
(void)va_arg(va, int); // no-warning
|
|
va_end(va);
|
|
}
|
|
|
|
|
|
void has_conditional_return(int fst, ...) {
|
|
// The va_arg call is not always executed, so don't report this function.
|
|
// Actually understanding the conditional logic is infeasible, so the checker
|
|
// should accept any conditional logic that sometimes avoids the va_arg().
|
|
va_list va;
|
|
if (fst < 0)
|
|
return;
|
|
va_start(va, fst);
|
|
(void)va_arg(va, int); // no-warning
|
|
va_end(va);
|
|
}
|
|
|
|
|
|
void has_conditional_logic(int fst, ...) {
|
|
// As static analyzer only follows one execution path, it cannot see that
|
|
// va_arg() is always executed in this function. Theoretically it would be
|
|
// better to produce a report here, but implementing this would be a very
|
|
// ineffective use of development effort.
|
|
va_list va;
|
|
if (fst < 0)
|
|
fst = -fst;
|
|
va_start(va, fst);
|
|
(void)va_arg(va, int); // no-warning
|
|
va_end(va);
|
|
}
|
|
|
|
|
|
#define IS_FEATURE_ENABLED 1
|
|
void has_constant_condition(int fst, ...) {
|
|
// This checker doesn't have special logic for recognizing and ignoring
|
|
// conditionals that can be resolved at compile time. This feature could be
|
|
// added in the future.
|
|
va_list va;
|
|
if (IS_FEATURE_ENABLED) {
|
|
va_start(va, fst);
|
|
(void)va_arg(va, int); // no-warning
|
|
va_end(va);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void caller_has_conditional_logic(int fst, ...) {
|
|
// This function is identical to never_called(), but it is used by a function
|
|
// that performs a state split before the call.
|
|
// This test validates that state splits _before_ the variadic call (which
|
|
// are irrelevant) do not influence the report creation. This is basically a
|
|
// workaround for the fact that not all functions are entrypoints and before
|
|
// reaching "our" variadic function the analyzer may go through arbitrary
|
|
// irrelevant code.
|
|
va_list va;
|
|
va_start(va, fst);
|
|
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
|
|
// expected-note@-1 {{Calls to 'caller_has_conditional_logic' always reach this va_arg() expression, so calling 'caller_has_conditional_logic' with no variadic arguments would be undefined behavior}}
|
|
va_end(va);
|
|
}
|
|
|
|
void caller_with_conditional_logic(int flag) {
|
|
if (flag)
|
|
caller_has_conditional_logic(1, 2, 3);
|
|
}
|
|
|
|
|
|
int has_shortcircuiting_operator(int fst, ...) {
|
|
// In addition to 'if' and similar statements, ternary operators and
|
|
// shortcircuiting logical operators can also ensure that the use of va_arg()
|
|
// is not unconditional. (These all behave identically in the BranchCondition
|
|
// callback, so this simple testcase is mostly for documentation purposes.)
|
|
va_list va;
|
|
int x;
|
|
va_start(va, fst);
|
|
x = fst || va_arg(va, int); // no-warning
|
|
va_end(va);
|
|
return x;
|
|
}
|
|
|
|
|
|
void validate_argument(int fst) {
|
|
if (fst <= 0) {
|
|
printf("Negative first argument is illegal: %d!\n", fst);
|
|
abort();
|
|
}
|
|
}
|
|
|
|
void noreturn_in_function(int fst, ...) {
|
|
// This function calls a function which may call the noreturn function
|
|
// abort() before reaching the va_arg() call, so the checker doesn't produce
|
|
// a warning. This behavior is important e.g. for cases when a function uses
|
|
// assertions to mark preconditions.
|
|
va_list va;
|
|
validate_argument(fst);
|
|
va_start(va, fst);
|
|
(void)va_arg(va, int); // no-warning
|
|
va_end(va);
|
|
}
|
|
|
|
|
|
void print_negative(int fst) {
|
|
if (fst <= 0) {
|
|
printf("First argument is negative: %d!\n", fst);
|
|
}
|
|
}
|
|
|
|
void conditional_in_function(int fst, ...) {
|
|
// This function is very similar to noreturn_in_function(), but the called
|
|
// function always returns. However, the analyzer doesn't see this difference
|
|
// (because it follows a _single_ execution path and cannot see the
|
|
// alternative) so the checker behaves as in noreturn_in_function.
|
|
va_list va;
|
|
print_negative(fst);
|
|
va_start(va, fst);
|
|
(void)va_arg(va, int); // no-warning
|
|
va_end(va);
|
|
}
|
|
|
|
|
|
void defined_in_different_tu(int);
|
|
|
|
void unknown_call(int fst, ...) {
|
|
// This function is very similar to noreturn_in_function() or
|
|
// conditional_in_function() but the definition of the called function is not
|
|
// visible for the analyzer. In theis situation the analyzer ignores the call
|
|
// and reports the va_arg() expression that unconditionally appears after it.
|
|
// This would be incorrect behavior only in a situation when the called
|
|
// function can return _and_ can be noreturn which should be fairly rare.
|
|
va_list va;
|
|
defined_in_different_tu(fst);
|
|
va_start(va, fst);
|
|
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
|
|
// expected-note@-1 {{Calls to 'unknown_call' always reach this va_arg() expression, so calling 'unknown_call' with no variadic arguments would be undefined behavior}}
|
|
va_end(va);
|
|
}
|
|
|
|
|
|
void trivial_variadic(int fst, ...) {
|
|
// Do nothing.
|
|
}
|
|
|
|
void calls_other_variadic(int fst, ...) {
|
|
// As the 'HasUnconditionalPath' can "remember" only one variadic function,
|
|
// I would expect that the presence of 'trivial_variadic' would prevent the
|
|
// checker from reporting the unconditional use of va_arg() in this function.
|
|
// However, for some unclear reason the checker is still able to produce this
|
|
// (true positive) report.
|
|
va_list va;
|
|
trivial_variadic(fst, 2);
|
|
va_start(va, fst);
|
|
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
|
|
// expected-note@-1 {{Calls to 'calls_other_variadic' always reach this va_arg() expression, so calling 'calls_other_variadic' with no variadic arguments would be undefined behavior}}
|
|
va_end(va);
|
|
}
|
|
|
|
int get_int_from_va_list(va_list *va) {
|
|
// This checker can report va_arg() expressions that are unconditionally
|
|
// reached from a variadic function, even if they are located in a different
|
|
// function. In this case the originating variadic function is marked with a
|
|
// note (to make it easier to find).
|
|
return va_arg(*va, int); // expected-warning{{Unconditional use of va_arg()}}
|
|
// expected-note@-1 {{Calls to 'uses_wrapped_va_arg' always reach this va_arg() expression, so calling 'uses_wrapped_va_arg' with no variadic arguments would be undefined behavior}}
|
|
}
|
|
|
|
void uses_wrapped_va_arg(int fst, ...) {
|
|
// expected-note@-1 {{Variadic function 'uses_wrapped_va_arg' is defined here}}
|
|
va_list va;
|
|
va_start(va, fst);
|
|
(void)get_int_from_va_list(&va);
|
|
va_end(va);
|
|
}
|
|
|
|
int standalone_non_variadic(va_list *va) {
|
|
// A va_arg() expression is only reported if it is unconditionally reached
|
|
// from the beginning of a _variadic_ function. Functions that use va_arg()
|
|
// on a va_list obtained from other sources (e.g. as an argument) are
|
|
// presumed to be small helper subroutines of a complex variadic function, so
|
|
// we do not report cases where the va_arg() is unconditionally reached from
|
|
// the beginning of a _non-variadic_ function.
|
|
return va_arg(*va, int); //no-warning
|
|
}
|
|
|
|
int variadic_but_also_takes_va_list(va_list *va, ...) {
|
|
// The checker just looks for va_arg() calls without checking the origin of
|
|
// their argument, so in this (very artifical) example it produces a
|
|
// result that is arguably a false positive.
|
|
return va_arg(*va, int); // expected-warning{{Unconditional use of va_arg()}}
|
|
// expected-note@-1 {{Calls to 'variadic_but_also_takes_va_list' always reach this va_arg() expression, so calling 'variadic_but_also_takes_va_list' with no variadic arguments would be undefined behavior}}
|
|
}
|