diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 105fe77eba73a..a79ee3ce8b0b7 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -17,7 +17,7 @@ use crate::session_diagnostics::{ AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic, ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier, InvalidMachoSection, InvalidMachoSectionReason, LinkFrameworkApple, LinkOrdinalOutOfRange, - LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, + LinkRequiresName, MultipleModifiers, NullOnLinkName, NullOnLinkSection, RawDylibOnlyWindows, WholeArchiveNeedsStatic, }; @@ -46,6 +46,19 @@ impl SingleAttributeParser for LinkNameParser { return None; }; + if name.as_str().contains('\0') { + // `#[link_name = ...]` will be converted to a null-terminated string, + // so it may not contain any null characters. + cx.emit_err(NullOnLinkName { span: nv.value_span }); + return None; + } + if name.is_empty() { + // Otherwise LLVM will just make up a name and the linker will fail + // to find an empty symbol name. + cx.emit_err(EmptyLinkName { span: nv.value_span }); + return None; + } + Some(LinkName { name, span: cx.attr_span }) } } @@ -222,7 +235,7 @@ impl CombineAttributeParser for LinkParser { if wasm_import_module.is_some() { (name, kind) = (wasm_import_module, Some(NativeLibKind::WasmImportModule)); } - let Some((name, name_span)) = name else { + let Some((name, _name_span)) = name else { cx.emit_err(LinkRequiresName { span: cx.attr_span }); return None; }; @@ -234,12 +247,6 @@ impl CombineAttributeParser for LinkParser { } } - if let Some(NativeLibKind::RawDylib { .. }) = kind - && name.as_str().contains('\0') - { - cx.emit_err(RawDylibNoNul { span: name_span }); - } - Some(LinkEntry { span: cx.attr_span, kind: kind.unwrap_or(NativeLibKind::Unspecified), @@ -270,9 +277,13 @@ impl LinkParser { return false; }; + if link_name.as_str().contains('\0') { + cx.emit_err(NullOnLinkName { span: nv.value_span }); + } if link_name.is_empty() { cx.emit_err(EmptyLinkName { span: nv.value_span }); } + *name = Some((link_name, nv.value_span)); true } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 0a9c96033257d..5de4a425c9fd8 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -410,6 +410,13 @@ pub(crate) struct NullOnLinkSection { pub span: Span, } +#[derive(Diagnostic)] +#[diag("link name may not contain null characters", code = E0648)] +pub(crate) struct NullOnLinkName { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("`objc::class!` may not contain null characters")] pub(crate) struct NullOnObjcClass { @@ -984,13 +991,6 @@ pub(crate) struct LinkRequiresName { pub span: Span, } -#[derive(Diagnostic)] -#[diag("link name must not contain NUL characters if link kind is `raw-dylib`")] -pub(crate) struct RawDylibNoNul { - #[primary_span] - pub span: Span, -} - #[derive(Diagnostic)] #[diag("link kind `raw-dylib` is only supported on Windows targets", code = E0455)] pub(crate) struct RawDylibOnlyWindows { diff --git a/tests/ui/attributes/invalid-link-name.rs b/tests/ui/attributes/invalid-link-name.rs new file mode 100644 index 0000000000000..579100914d3c4 --- /dev/null +++ b/tests/ui/attributes/invalid-link-name.rs @@ -0,0 +1,54 @@ +#![crate_type = "lib"] + +#[link(name = "")] +//~^ ERROR link name must not be empty +unsafe extern "C" { + #[link_name = ""] + //~^ ERROR link name must not be empty + safe fn empty(); +} + +#[link(name = " ")] +unsafe extern "C" { + #[link_name = " "] + safe fn this_is_fine(); +} + +#[export_name = " "] +extern "C" fn bar() -> i32 { + 42 +} + +#[link(name = "\0")] +//~^ ERROR link name may not contain null characters +unsafe extern "C" {} + +#[link(name = "foo\0")] +//~^ ERROR link name may not contain null characters +unsafe extern "C" {} + +#[link(name = "\0foo")] +//~^ ERROR link name may not contain null characters +unsafe extern "C" {} + +#[link(name = "fo\0o")] +//~^ ERROR link name may not contain null characters +unsafe extern "C" {} + +unsafe extern "C" { + #[link_name = "\0"] + //~^ ERROR link name may not contain null characters + safe fn empty_null(); + + #[link_name = "foo\0"] + //~^ ERROR link name may not contain null characters + safe fn trailing_null(); + + #[link_name = "\0foo"] + //~^ ERROR link name may not contain null characters + safe fn leading_null(); + + #[link_name = "fo\0o"] + //~^ ERROR link name may not contain null characters + safe fn middle_null(); +} diff --git a/tests/ui/attributes/invalid-link-name.stderr b/tests/ui/attributes/invalid-link-name.stderr new file mode 100644 index 0000000000000..b31c1d53bd81e --- /dev/null +++ b/tests/ui/attributes/invalid-link-name.stderr @@ -0,0 +1,64 @@ +error[E0454]: link name must not be empty + --> $DIR/invalid-link-name.rs:3:15 + | +LL | #[link(name = "")] + | ^^ empty link name + +error[E0454]: link name must not be empty + --> $DIR/invalid-link-name.rs:6:19 + | +LL | #[link_name = ""] + | ^^ empty link name + +error[E0648]: link name may not contain null characters + --> $DIR/invalid-link-name.rs:22:15 + | +LL | #[link(name = "\0")] + | ^^^^ + +error[E0648]: link name may not contain null characters + --> $DIR/invalid-link-name.rs:26:15 + | +LL | #[link(name = "foo\0")] + | ^^^^^^^ + +error[E0648]: link name may not contain null characters + --> $DIR/invalid-link-name.rs:30:15 + | +LL | #[link(name = "\0foo")] + | ^^^^^^^ + +error[E0648]: link name may not contain null characters + --> $DIR/invalid-link-name.rs:34:15 + | +LL | #[link(name = "fo\0o")] + | ^^^^^^^ + +error[E0648]: link name may not contain null characters + --> $DIR/invalid-link-name.rs:39:19 + | +LL | #[link_name = "\0"] + | ^^^^ + +error[E0648]: link name may not contain null characters + --> $DIR/invalid-link-name.rs:43:19 + | +LL | #[link_name = "foo\0"] + | ^^^^^^^ + +error[E0648]: link name may not contain null characters + --> $DIR/invalid-link-name.rs:47:19 + | +LL | #[link_name = "\0foo"] + | ^^^^^^^ + +error[E0648]: link name may not contain null characters + --> $DIR/invalid-link-name.rs:51:19 + | +LL | #[link_name = "fo\0o"] + | ^^^^^^^ + +error: aborting due to 10 previous errors + +Some errors have detailed explanations: E0454, E0648. +For more information about an error, try `rustc --explain E0454`. diff --git a/tests/ui/linkage-attr/raw-dylib/elf/malformed-link-name.rs b/tests/ui/linkage-attr/raw-dylib/elf/malformed-link-name.rs index 46e3798284b25..02262c2378c83 100644 --- a/tests/ui/linkage-attr/raw-dylib/elf/malformed-link-name.rs +++ b/tests/ui/linkage-attr/raw-dylib/elf/malformed-link-name.rs @@ -11,10 +11,8 @@ unsafe extern "C" { pub safe fn exit_0(status: i32) -> !; //~ ERROR link name must be well-formed if link kind is `raw-dylib` #[link_name = "@GLIBC_2.2.5"] pub safe fn exit_1(status: i32) -> !; //~ ERROR link name must be well-formed if link kind is `raw-dylib` - #[link_name = "ex\0it@GLIBC_2.2.5"] - pub safe fn exit_2(status: i32) -> !; //~ ERROR link name must be well-formed if link kind is `raw-dylib` #[link_name = "exit@@GLIBC_2.2.5"] - pub safe fn exit_3(status: i32) -> !; //~ ERROR link name must be well-formed if link kind is `raw-dylib` + pub safe fn exit_2(status: i32) -> !; //~ ERROR link name must be well-formed if link kind is `raw-dylib` } fn main() {} diff --git a/tests/ui/linkage-attr/raw-dylib/elf/malformed-link-name.stderr b/tests/ui/linkage-attr/raw-dylib/elf/malformed-link-name.stderr index 5a979e7a3b1af..f83c63f06b8ba 100644 --- a/tests/ui/linkage-attr/raw-dylib/elf/malformed-link-name.stderr +++ b/tests/ui/linkage-attr/raw-dylib/elf/malformed-link-name.stderr @@ -16,11 +16,5 @@ error: link name must be well-formed if link kind is `raw-dylib` LL | pub safe fn exit_2(status: i32) -> !; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: link name must be well-formed if link kind is `raw-dylib` - --> $DIR/malformed-link-name.rs:17:5 - | -LL | pub safe fn exit_3(status: i32) -> !; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors