Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Verifier] Reject va_start in non-variadic function #88809

Merged
merged 1 commit into from
Apr 16, 2024

Conversation

JonChesterfield
Copy link
Collaborator

A va_start intrinsic lowers to something derived from the variadic parameter to the function. If there is no such parameter, it can't lower meaningfully. Clang sema rejects the same with error: 'va_start' used in function with fixed args.

Moves the existing lint warning into a verifier error. Updates the one lit test that had a va_start in a non-variadic function.

@llvmbot
Copy link
Collaborator

llvmbot commented Apr 15, 2024

@llvm/pr-subscribers-llvm-analysis
@llvm/pr-subscribers-llvm-globalisel
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-backend-aarch64

Author: Jon Chesterfield (JonChesterfield)

Changes

A va_start intrinsic lowers to something derived from the variadic parameter to the function. If there is no such parameter, it can't lower meaningfully. Clang sema rejects the same with error: 'va_start' used in function with fixed args.

Moves the existing lint warning into a verifier error. Updates the one lit test that had a va_start in a non-variadic function.


Full diff: https://github.com/llvm/llvm-project/pull/88809.diff

5 Files Affected:

  • (modified) llvm/lib/Analysis/Lint.cpp (+1-4)
  • (modified) llvm/lib/IR/Verifier.cpp (+5)
  • (modified) llvm/test/CodeGen/AArch64/GlobalISel/vastart.ll (+1-1)
  • (modified) llvm/test/Other/lint.ll (-7)
  • (added) llvm/test/Verifier/variadic.ll (+8)
diff --git a/llvm/lib/Analysis/Lint.cpp b/llvm/lib/Analysis/Lint.cpp
index 0694c2995dfcce..1ab856ac8830a9 100644
--- a/llvm/lib/Analysis/Lint.cpp
+++ b/llvm/lib/Analysis/Lint.cpp
@@ -350,10 +350,7 @@ void Lint::visitCallBase(CallBase &I) {
     }
 
     case Intrinsic::vastart:
-      Check(I.getParent()->getParent()->isVarArg(),
-            "Undefined behavior: va_start called in a non-varargs function",
-            &I);
-
+      // vastart in non-varargs function is rejected by the verifier
       visitMemoryReference(I, MemoryLocation::getForArgument(&I, 0, TLI),
                            std::nullopt, nullptr, MemRef::Read | MemRef::Write);
       break;
diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 33f358440a312d..8720bfc2093b5b 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -5769,6 +5769,11 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
 
     break;
   }
+  case Intrinsic::vastart: {
+    Check(Call.getFunction()->isVarArg(),
+          "va_start called in a non-varargs function");
+    break;
+  }
   case Intrinsic::vector_reduce_and:
   case Intrinsic::vector_reduce_or:
   case Intrinsic::vector_reduce_xor:
diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/vastart.ll b/llvm/test/CodeGen/AArch64/GlobalISel/vastart.ll
index bd576d0f70e9c1..8c6e01d934c2d8 100644
--- a/llvm/test/CodeGen/AArch64/GlobalISel/vastart.ll
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/vastart.ll
@@ -3,7 +3,7 @@
 
 
 declare void @llvm.va_start(ptr)
-define void @test_va_start(ptr %list) {
+define void @test_va_start(ptr %list, ...) {
 ; CHECK-LABEL: name: test_va_start
 ; CHECK: [[LIST:%[0-9]+]]:_(p0) = COPY $x0
 ; CHECK-IOS: G_VASTART [[LIST]](p0) :: (store (s64) into %ir.list, align 1)
diff --git a/llvm/test/Other/lint.ll b/llvm/test/Other/lint.ll
index 6b31b31a78c98a..6fd2d40cd2f298 100644
--- a/llvm/test/Other/lint.ll
+++ b/llvm/test/Other/lint.ll
@@ -124,13 +124,6 @@ define void @0() nounwind {
   ret void
 }
 
-; CHECK: va_start called in a non-varargs function
-declare void @llvm.va_start(ptr)
-define void @not_vararg(ptr %p) nounwind {
-  call void @llvm.va_start(ptr %p)
-  ret void
-}
-
 ; CHECK: Undefined behavior: Branch to non-blockaddress
 define void @use_indbr() {
   indirectbr ptr @foo, [label %block]
diff --git a/llvm/test/Verifier/variadic.ll b/llvm/test/Verifier/variadic.ll
new file mode 100644
index 00000000000000..55e4a4da0a9203
--- /dev/null
+++ b/llvm/test/Verifier/variadic.ll
@@ -0,0 +1,8 @@
+; RUN: not opt -S -passes=verify 2>&1 < %s | FileCheck %s
+
+; CHECK: va_start called in a non-varargs function
+declare void @llvm.va_start(ptr)
+define void @not_vararg(ptr %p) nounwind {
+  call void @llvm.va_start(ptr %p)
+  ret void
+}

@efriedma-quic
Copy link
Collaborator

Do we want AutoUpgrade for this, or should we assume this doesn't show up in any existing bitcode files anyone cares about?

Otherwise looks fine.

@JonChesterfield
Copy link
Collaborator Author

The best case interpretation for the construct is UB - we could do something like replace it with undef if it occurs, but as the construct is nonsense and clang rejects it in sema anyway, I don't think this arises in reality. I'd like it to be an IR invariant so I don't have to handle it in varargs lowering.

Copy link
Collaborator

@efriedma-quic efriedma-quic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With autoupgrade, we'd replace it with undef or something like that, so you'd maintain the invariant in memory, so you maintain the invariant either way. The real question is whether such invalid constructs have somehow leaked into existing IR files despite clang not generating them.

That said, thinking about it a bit more, it seems pretty unlikely; most IR using va_list is going to originate from C/Rust, and they can't ever generate bad IR like this as far as I know. And I think LLVM transformations have been pretty consistent about not constructing invalid code through inlining etc.

So LGTM

@JonChesterfield JonChesterfield merged commit 61717c1 into llvm:main Apr 16, 2024
8 of 9 checks passed
@JonChesterfield JonChesterfield deleted the jc_vastart_verifier branch April 16, 2024 09:34
JonChesterfield added a commit that referenced this pull request Apr 16, 2024
@Hardcode84
Copy link
Contributor

This change broke MLIR tests (PR buildkite failed as well), they are probably needeed to be updated

******************** TEST 'MLIR :: Target/LLVMIR/Import/intrinsic.ll' FAILED ********************
Exit Code: 2

Command Output (stdout):
--
# RUN: at line 1
/var/lib/buildkite-agent/builds/linux-56-59b8f5d88-6mhbj-1/llvm-project/github-pull-requests/build/bin/mlir-translate -import-llvm /var/lib/buildkite-agent/builds/linux-56-59b8f5d88-6mhbj-1/llvm-project/github-pull-requests/mlir/test/Target/LLVMIR/Import/intrinsic.ll | /var/lib/buildkite-agent/builds/linux-56-59b8f5d88-6mhbj-1/llvm-project/github-pull-requests/build/bin/FileCheck /var/lib/buildkite-agent/builds/linux-56-59b8f5d88-6mhbj-1/llvm-project/github-pull-requests/mlir/test/Target/LLVMIR/Import/intrinsic.ll
# executed command: /var/lib/buildkite-agent/builds/linux-56-59b8f5d88-6mhbj-1/llvm-project/github-pull-requests/build/bin/mlir-translate -import-llvm /var/lib/buildkite-agent/builds/linux-56-59b8f5d88-6mhbj-1/llvm-project/github-pull-requests/mlir/test/Target/LLVMIR/Import/intrinsic.ll
# .---command stderr------------
# | va_start called in a non-varargs function
# `-----------------------------
# error: command failed with exit status: 1
# executed command: /var/lib/buildkite-agent/builds/linux-56-59b8f5d88-6mhbj-1/llvm-project/github-pull-requests/build/bin/FileCheck /var/lib/buildkite-agent/builds/linux-56-59b8f5d88-6mhbj-1/llvm-project/github-pull-requests/mlir/test/Target/LLVMIR/Import/intrinsic.ll
# .---command stderr------------
# | FileCheck error: '<stdin>' is empty.
# | FileCheck command line:  /var/lib/buildkite-agent/builds/linux-56-59b8f5d88-6mhbj-1/llvm-project/github-pull-requests/build/bin/FileCheck /var/lib/buildkite-agent/builds/linux-56-59b8f5d88-6mhbj-1/llvm-project/github-pull-requests/mlir/test/Target/LLVMIR/Import/intrinsic.ll
# `-----------------------------
# error: command failed with exit status: 2

JonChesterfield added a commit that referenced this pull request Apr 16, 2024
This reverts commit f4960da.
Includes a fix for the MLIR test case.
@AaronBallman
Copy link
Collaborator

Please make sure to verify this works in C23 mode as well. e.g.,

void func(...) {}

void foo() {
  va_list list;
  va_start(list);
  ...
  va_end(list);
}

I suspect it will Just Work™ but an explicit test for that scenario would be useful IMO.

@JonChesterfield
Copy link
Collaborator Author

@AaronBallman I don't understand what you're asking here. C23 didn't change anything applicable to this as far as I'm aware. If it did, the problem would be in clang sema, not in the IR verifier.

Prepending the stdarg include and specifying c23 https://godbolt.org/z/Eqr3vTa9K gets the expected errors:

<source>:8:3: error: 'va_start' used in function with fixed args
    8 |   va_start(list);
      |   ^
/opt/compiler-explorer/clang-assertions-trunk-20240416/lib/clang/19/include/__stdarg_va_arg.h:14:27: note: expanded from macro 'va_start'
   14 | #define va_start(ap, ...) __builtin_va_start(ap, 0)
      |                           ^
<source>:9:3: error: expected expression
    9 |   ...
      |   ^
2 errors generated.
Compiler returned: 1

Did you want void foo(...), i.e. have no fixed arguments? That creates valid IR with the ... expression removed,

define dso_local void @foo(...) #0 {
entry:
  %list = alloca [1 x %struct.__va_list_tag], align 16
  %arraydecay = getelementptr inbounds [1 x %struct.__va_list_tag], ptr %list, i64 0, i64 0
  call void @llvm.va_start.p0(ptr %arraydecay)
  %arraydecay1 = getelementptr inbounds [1 x %struct.__va_list_tag], ptr %list, i64 0, i64 0
  call void @llvm.va_end.p0(ptr %arraydecay1)
  ret void
}

which is fine, it's a va_start in a variadic function.

@AaronBallman
Copy link
Collaborator

@AaronBallman I don't understand what you're asking here. C23 didn't change anything applicable to this as far as I'm aware. If it did, the problem would be in clang sema, not in the IR verifier.

Prepending the stdarg include and specifying c23 https://godbolt.org/z/Eqr3vTa9K gets the expected errors:

<source>:8:3: error: 'va_start' used in function with fixed args
    8 |   va_start(list);
      |   ^
/opt/compiler-explorer/clang-assertions-trunk-20240416/lib/clang/19/include/__stdarg_va_arg.h:14:27: note: expanded from macro 'va_start'
   14 | #define va_start(ap, ...) __builtin_va_start(ap, 0)
      |                           ^
<source>:9:3: error: expected expression
    9 |   ...
      |   ^
2 errors generated.
Compiler returned: 1

Did you want void foo(...), i.e. have no fixed arguments? That creates valid IR with the ... expression removed,

define dso_local void @foo(...) #0 {
entry:
  %list = alloca [1 x %struct.__va_list_tag], align 16
  %arraydecay = getelementptr inbounds [1 x %struct.__va_list_tag], ptr %list, i64 0, i64 0
  call void @llvm.va_start.p0(ptr %arraydecay)
  %arraydecay1 = getelementptr inbounds [1 x %struct.__va_list_tag], ptr %list, i64 0, i64 0
  call void @llvm.va_end.p0(ptr %arraydecay1)
  ret void
}

which is fine, it's a va_start in a variadic function.

Ugh, sorry for the confusion with my example, that was... egregiously confusing. :-D I meant:

/* Type your code here, or load an example. */
#include <stdarg.h>

void func(...) {
  va_list list;
  va_start(list);
  ...
  va_end(list);
}

void foo() {
  func();
}

and the only goal for the request was to make sure that the IR verifier still considers func to be variadic despite the lack of any parameters other than ... (which I presume it will handle just fine). If you don't think that's a reasonable test to add, I also don't insist either. Sorry for the confusion!

JonChesterfield added a commit to JonChesterfield/llvm-project that referenced this pull request Jun 6, 2024
)"

This reverts commit f4960da.
Includes a fix for the MLIR test case.
searlmc1 pushed a commit to ROCm/llvm-project that referenced this pull request Jun 14, 2024
)"

This reverts commit f4960da.
Includes a fix for the MLIR test case.

Change-Id: I0dbfcd5f9d95813047626b2b885313c8d52ba29f
jrbyrnes pushed a commit to jrbyrnes/llvm-project that referenced this pull request Jul 17, 2024
)"

This reverts commit f4960da.
Includes a fix for the MLIR test case.

Change-Id: I0dbfcd5f9d95813047626b2b885313c8d52ba29f
cferry-AMD pushed a commit to Xilinx/llvm-project that referenced this pull request Aug 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants