https://github.com/akash-manna-sky updated https://github.com/llvm/llvm-project/pull/225306
>From 055bce5fd8fe4a0a22b0f3f546be7ba9d003cce8 Mon Sep 17 00:00:00 2001 From: Akash Manna <[email protected]> Date: Tue, 22 Sep 2026 11:51:38 +0530 Subject: [PATCH] [clang][Sema] Diagnose nested assembler dialect alternatives in inline asm strings GCCAsmStmt::AnalyzeAsmString rewrote {, | and } into the backend's $(, $| and $) markers without checking their structure, so a nested {a{b}} alternative reached the AsmPrinter and was reported with report_fatal_error. Track the open alternative while scanning and report nested or unterminated alternatives as regular Sema errors at their location in the string, matching GCC, which rejects both. Fixes #204773 --- clang/docs/ReleaseNotes.md | 4 +++ .../include/clang/Basic/DiagnosticASTKinds.td | 4 +++ clang/lib/AST/Stmt.cpp | 29 +++++++++++++++++-- clang/test/Sema/asm.c | 13 +++++++++ clang/test/Sema/inline-asm-validate-aarch64.c | 4 +++ 5 files changed, 52 insertions(+), 2 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index f4a34a37aff52..61dacc99a9f2b 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -451,6 +451,10 @@ features cannot lower the translation-unit ABI level; - Clang now rejects inline asm constraints and clobbers that contain an embedded null character, instead of silently truncating them. (#GH173900) +- Clang now rejects nested or unterminated assembler dialect alternatives + (`{att|intel}`) in an inline asm string, instead of producing a backend fatal + error or silently emitting the wrong dialect. (#GH204773) + - Added `-Wstringop-overread` to warn when `memcpy`, `memmove`, `memcmp`, and related builtins read more bytes than the source buffer size (#GH83728). diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td index 0aca1f75428f8..ac8e7ed7c2f6d 100644 --- a/clang/include/clang/Basic/DiagnosticASTKinds.td +++ b/clang/include/clang/Basic/DiagnosticASTKinds.td @@ -461,6 +461,10 @@ let CategoryName = "Inline Assembly Issue" in { "empty symbolic operand name in inline assembly string">; def err_asm_invalid_operand_number : Error< "invalid operand number in inline asm string">; + def err_asm_nested_dialect_alternatives : Error< + "nested assembler dialect alternatives in inline assembly string">; + def err_asm_unterminated_dialect_alternative : Error< + "unterminated assembler dialect alternative in inline assembly string">; } // vtable related. diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index 15d0e6435aaf3..4513e8d6f665f 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -699,12 +699,19 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces, bool HasVariants = !C.getTargetInfo().hasNoAsmVariants(); + // Offset of the '{' opening the current {a|b|c} dialect alternative, if any. + std::optional<unsigned> VariantStartOffs; + unsigned LastAsmStringToken = 0; unsigned LastAsmStringOffset = 0; while (true) { // Done with the string? if (CurPtr == StrEnd) { + if (VariantStartOffs) { + DiagOffs = *VariantStartOffs; + return diag::err_asm_unterminated_dialect_alternative; + } if (!CurStringPiece.empty()) Pieces.push_back(AsmStringPiece(CurStringPiece)); return 0; @@ -713,9 +720,27 @@ unsigned GCCAsmStmt::AnalyzeAsmString(SmallVectorImpl<AsmStringPiece>&Pieces, char CurChar = *CurPtr++; switch (CurChar) { case '$': CurStringPiece += "$$"; continue; - case '{': CurStringPiece += (HasVariants ? "$(" : "{"); continue; + case '{': + if (!HasVariants) { + CurStringPiece += '{'; + continue; + } + if (VariantStartOffs) { + DiagOffs = CurPtr - StrStart - 1; + return diag::err_asm_nested_dialect_alternatives; + } + VariantStartOffs = CurPtr - StrStart - 1; + CurStringPiece += "$("; + continue; case '|': CurStringPiece += (HasVariants ? "$|" : "|"); continue; - case '}': CurStringPiece += (HasVariants ? "$)" : "}"); continue; + case '}': + if (!HasVariants) { + CurStringPiece += '}'; + continue; + } + VariantStartOffs.reset(); + CurStringPiece += "$)"; + continue; case '%': break; default: diff --git a/clang/test/Sema/asm.c b/clang/test/Sema/asm.c index cc9acac1e169d..4434adfeee5e7 100644 --- a/clang/test/Sema/asm.c +++ b/clang/test/Sema/asm.c @@ -404,3 +404,16 @@ void test20(char x) { asm ("fabs" : "=t" (d): "0" (v)); // expected-error {{unsupported inline asm: input with type 'int2' (vector of 2 'int' values) matching output with type 'double'}} asm ("fabs" : "=t" (v): "0" (d)); // expected-error {{unsupported inline asm: input with type 'double' matching output with type 'int2' (vector of 2 'int' values)}} } + +// GH204773 +void test21(int x) { + asm ("{cmpl{l}\t$d,%c0;je\t1f;addw{l}\t$d,%c0;jmp\t2f;1:decl\t%c0;2:}" : : "g"(x)); // expected-error {{nested assembler dialect alternatives in inline assembly string}} + asm ("{addl %0, %0|add %0, %0}" : : "r"(x)); // ok + asm ("{a|b}{c|d}" : : "r"(x)); // ok + asm ("{a|b" : : "r"(x)); // expected-error {{unterminated assembler dialect alternative in inline assembly string}} + asm ("{" : : "r"(x)); // expected-error {{unterminated assembler dialect alternative in inline assembly string}} + asm ("a}b|c" : : "r"(x)); // ok + asm ("%{%{%}%}" : : "r"(x)); // ok, escaped braces + asm ("{%{a%}|b}" : : "r"(x)); // ok + asm ("{{"); // ok, simple asm +} diff --git a/clang/test/Sema/inline-asm-validate-aarch64.c b/clang/test/Sema/inline-asm-validate-aarch64.c index 1e753d40d8ca0..da4e59859743d 100644 --- a/clang/test/Sema/inline-asm-validate-aarch64.c +++ b/clang/test/Sema/inline-asm-validate-aarch64.c @@ -10,6 +10,10 @@ void test_s(int i) { /// Codegen error asm("" :: "S"(i)); asm("" :: "S"(test_s(i))); // expected-error{{invalid type 'void' in asm input for constraint 'S'}} + + // GH204773: braces are ordinary characters on this target. + asm("{a{b}}" :: "r"(i)); + asm("{" :: "r"(i)); } #else uint8_t constraint_r(uint8_t *addr) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
