Skip to content

Commit 4ebd736

Browse files
bors[bot]Bromeon
andauthored
Merge #325
325: Address sanitizers for Rust r=Bromeon a=Bromeon We have missed a lot of UB that occurred only in Rust code and wasn't detected by AddressSanitizer outside of C++ code. This PR enables ASan for Rust code using `-Zsanitizer=address` in the two memcheck CI jobs. As such, this is a follow-up to #133. The CI currently fails because there is still some UB on `master`... ideally we can fix this soon. bors try Co-authored-by: Jan Haller <[email protected]>
2 parents 565bf10 + a3012f4 commit 4ebd736

File tree

10 files changed

+55
-33
lines changed

10 files changed

+55
-33
lines changed

.github/composite/godot-itest/action.yml

+24-5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ inputs:
4545
default: ''
4646
description: "Extra values for the RUSTFLAGS env var"
4747

48+
rust-target:
49+
required: false
50+
default: ''
51+
description: "If provided, acts as an argument for '--target', and results in output files written to ./target/{target}"
52+
4853
with-llvm:
4954
required: false
5055
default: ''
@@ -118,11 +123,23 @@ runs:
118123
shell: bash
119124

120125
- name: "Build gdext (itest)"
121-
run: |
122-
cargo build -p itest ${{ inputs.rust-extra-args }}
123-
shell: bash
124126
env:
125127
RUSTFLAGS: ${{ inputs.rust-env-rustflags }}
128+
TARGET: ${{ inputs.rust-target }}
129+
run: |
130+
targetArgs=""
131+
if [[ -n "$TARGET" ]]; then
132+
targetArgs="--target $TARGET"
133+
fi
134+
135+
cargo build -p itest ${{ inputs.rust-extra-args }} $targetArgs
136+
137+
# Instead of modifying .gdextension, rename the output directory
138+
if [[ -n "$TARGET" ]]; then
139+
rm -rf target/debug
140+
mv target/$TARGET/debug target
141+
fi
142+
shell: bash
126143

127144
# This step no longer fails if there's a diff, as we expect header to be forward-compatible; instead issues a warning
128145
# However, still fails if patch cannot be applied (conflict).
@@ -162,8 +179,10 @@ runs:
162179
run: |
163180
cd itest/godot
164181
echo "OUTCOME=itest" >> $GITHUB_ENV
165-
$GODOT4_BIN --headless ${{ inputs.godot-args }} 2>&1 | tee "${{ runner.temp }}/log.txt" | tee >(grep "SCRIPT ERROR:" -q && {
166-
printf "\n -- Godot engine encountered error, abort...\n";
182+
$GODOT4_BIN --headless ${{ inputs.godot-args }} 2>&1 \
183+
| tee "${{ runner.temp }}/log.txt" \
184+
| tee >(grep -E "SCRIPT ERROR:|Can't open dynamic library" -q && {
185+
printf "\n::error::godot-itest: unrecoverable Godot error, abort...\n";
167186
pkill godot
168187
echo "OUTCOME=godot-runtime" >> $GITHUB_ENV
169188
exit 2

.github/composite/rust/action.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ runs:
3131
steps:
3232
- name: "Configure"
3333
id: configure
34+
env:
35+
CS: ${{ inputs.components }}
3436
run: |
3537
echo "Rust cache shared-key: '${{ runner.os }}-${{ inputs.rust }}${{ inputs.cache-key }}'"
36-
echo "components=$( for c in ${cs//,/ }; do echo -n ' --component' $c; done )" >> $GITHUB_OUTPUT
37-
env:
38-
cs: ${{ inputs.components }}
38+
echo "components=$( for c in ${CS//,/ }; do echo -n ' --component' $c; done )" >> $GITHUB_OUTPUT
3939
shell: bash
4040

4141
- name: "Rustup"

.github/workflows/full-ci.yml

+11-4
Original file line numberDiff line numberDiff line change
@@ -202,17 +202,22 @@ jobs:
202202
godot-prebuilt-patch: '4.1'
203203

204204
# Special Godot binaries compiled with AddressSanitizer/LeakSanitizer to detect UB/leaks.
205+
# See also https://rustc-dev-guide.rust-lang.org/sanitizers.html.
206+
#
205207
# Additionally, the Godot source is patched to make dlclose() a no-op, as unloading dynamic libraries loses stacktrace and
206208
# cause false positives like println!. See https://github.com/google/sanitizers/issues/89.
207-
# The gcc version can possibly be removed later, as it is slower and needs a larger artifact than the clang one.
209+
#
210+
# There is also a gcc variant besides clang, which is currently not used.
208211
- name: linux-memcheck-nightly
209212
os: ubuntu-20.04
210213
artifact-name: linux-memcheck-clang-nightly
211214
godot-binary: godot.linuxbsd.editor.dev.x86_64.llvm.san
212215
godot-args: -- --disallow-focus
213216
rust-toolchain: nightly
214-
rust-env-rustflags: -Zrandomize-layout
217+
rust-env-rustflags: -Zrandomize-layout -Zsanitizer=address
215218
rust-extra-args: --features godot/custom-godot
219+
# Sanitizers can't build proc-macros and build scripts; with --target, cargo ignores RUSTFLAGS for those two.
220+
rust-target: x86_64-unknown-linux-gnu
216221
godot-prebuilt-patch: '4.1'
217222

218223
# Linux under Godot 4.0.x
@@ -246,8 +251,9 @@ jobs:
246251
godot-binary: godot.linuxbsd.editor.dev.x86_64.llvm.san
247252
godot-args: -- --disallow-focus
248253
rust-toolchain: nightly
249-
rust-env-rustflags: -Zrandomize-layout
250-
254+
rust-env-rustflags: -Zrandomize-layout -Zsanitizer=address
255+
# Sanitizers can't build proc-macros and build scripts; with --target, cargo ignores RUSTFLAGS for those two.
256+
rust-target: x86_64-unknown-linux-gnu
251257

252258
steps:
253259
- uses: actions/checkout@v3
@@ -262,6 +268,7 @@ jobs:
262268
rust-extra-args: ${{ matrix.rust-extra-args }}
263269
rust-toolchain: ${{ matrix.rust-toolchain || 'stable' }}
264270
rust-env-rustflags: ${{ matrix.rust-env-rustflags }}
271+
rust-target: ${{ matrix.rust-target }}
265272
with-llvm: ${{ contains(matrix.name, 'macos') && contains(matrix.rust-extra-args, 'custom-godot') }}
266273
godot-check-header: ${{ matrix.godot-check-header }}
267274

godot-codegen/src/central_generator.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ fn make_central_items(api: &ExtensionApi, build_config: &str, ctx: &mut Context)
385385
.push(ident(&util::shout_to_pascal(name)));
386386
result
387387
.variant_op_enumerators_ord
388-
.push(Literal::i32_unsuffixed(op.value));
388+
.push(util::make_enumerator_ord(op.value));
389389
}
390390

391391
for enum_ in api.global_enums.iter() {
@@ -499,7 +499,7 @@ fn make_enumerator(
499499
let enumerator_name = &type_names.json_builtin_name;
500500
let pascal_name = to_pascal_case(enumerator_name);
501501
let rust_ty = to_rust_type(enumerator_name, None, ctx);
502-
let ord = Literal::i32_unsuffixed(value);
502+
let ord = util::make_enumerator_ord(value);
503503

504504
(ident(&pascal_name), rust_ty.to_token_stream(), ord)
505505
}

godot-codegen/src/class_generator.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ fn make_notification_enum(
684684
let mut notification_enumerators_ord = Vec::new();
685685
for (constant_ident, constant_value) in all_constants {
686686
notification_enumerators_pascal.push(constant_ident);
687-
notification_enumerators_ord.push(constant_value);
687+
notification_enumerators_ord.push(util::make_enumerator_ord(constant_value));
688688
}
689689

690690
let code = quote! {

godot-codegen/src/util.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub fn make_enum_definition(enum_: &Enum) -> TokenStream {
3535

3636
for enumerator in values {
3737
let name = make_enumerator_name(&enumerator.name, &enum_.name);
38-
let ordinal = Literal::i32_unsuffixed(enumerator.value);
38+
let ordinal = make_enumerator_ord(enumerator.value);
3939

4040
enumerators.push(quote! {
4141
pub const #name: Self = Self { ord: #ordinal };
@@ -120,7 +120,7 @@ pub fn make_enum_definition(enum_: &Enum) -> TokenStream {
120120
let err = sys::PrimitiveConversionError::new(via);
121121
let ord = i32::try_from(via).map_err(|_| err)?;
122122
<Self as crate::obj::EngineEnum>::try_from_ord(ord).ok_or(err)
123-
}
123+
}
124124

125125
fn try_into_via(self) -> std::result::Result<Self::Via, Self::IntoViaError> {
126126
Ok(<Self as crate::obj::EngineEnum>::ord(self).into())
@@ -486,6 +486,10 @@ pub fn parse_native_structures_format(input: &str) -> Option<Vec<NativeStructure
486486
.collect()
487487
}
488488

489+
pub(crate) fn make_enumerator_ord(ord: i32) -> Literal {
490+
Literal::i32_unsuffixed(ord)
491+
}
492+
489493
pub(crate) fn to_rust_expr(expr: &str, ty: &RustTy) -> TokenStream {
490494
// println!("\n> to_rust_expr({expr}, {ty:?})");
491495

godot-core/src/builtin/meta/signature.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub trait VarcallSignatureTuple: PtrcallSignatureTuple {
1818
// TODO(uninit) - can we use this for varcall/ptrcall?
1919
// ret: sys::GDExtensionUninitializedVariantPtr
2020
// ret: sys::GDExtensionUninitializedTypePtr
21-
unsafe fn varcall<C: GodotClass>(
21+
unsafe fn varcall(
2222
instance_ptr: sys::GDExtensionClassInstancePtr,
2323
args_ptr: *const sys::GDExtensionConstVariantPtr,
2424
ret: sys::GDExtensionVariantPtr,
@@ -35,7 +35,7 @@ pub trait PtrcallSignatureTuple {
3535

3636
// Note: this method imposes extra bounds on GodotFfi, which may not be implemented for user types.
3737
// We could fall back to varcalls in such cases, and not require GodotFfi categorically.
38-
unsafe fn ptrcall<C: GodotClass>(
38+
unsafe fn ptrcall(
3939
instance_ptr: sys::GDExtensionClassInstancePtr,
4040
args_ptr: *const sys::GDExtensionConstTypePtr,
4141
ret: sys::GDExtensionTypePtr,
@@ -63,7 +63,6 @@ pub trait PtrcallSignatureTuple {
6363
//
6464
use crate::builtin::meta::*;
6565
use crate::builtin::{FromVariant, ToVariant, Variant};
66-
use crate::obj::GodotClass;
6766

6867
macro_rules! impl_varcall_signature_for_tuple {
6968
(
@@ -102,7 +101,7 @@ macro_rules! impl_varcall_signature_for_tuple {
102101

103102

104103
#[inline]
105-
unsafe fn varcall<C : GodotClass>(
104+
unsafe fn varcall(
106105
instance_ptr: sys::GDExtensionClassInstancePtr,
107106
args_ptr: *const sys::GDExtensionConstVariantPtr,
108107
ret: sys::GDExtensionVariantPtr,
@@ -135,7 +134,7 @@ macro_rules! impl_ptrcall_signature_for_tuple {
135134
type Params = ($($Pn,)*);
136135
type Ret = $R;
137136

138-
unsafe fn ptrcall<C : GodotClass>(
137+
unsafe fn ptrcall(
139138
instance_ptr: sys::GDExtensionClassInstancePtr,
140139
args_ptr: *const sys::GDExtensionConstTypePtr,
141140
ret: sys::GDExtensionTypePtr,

godot-core/src/macros.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,12 @@ macro_rules! gdext_call_signature_method {
1212
(
1313
ptrcall,
1414
$Signature:ty,
15-
$Class:ty,
1615
$instance_ptr:ident, $args:ident, $ret:ident,
1716
$func:expr,
1817
$method_name:ident,
1918
$ptrcall_type:path
2019
) => {
21-
<$Signature as $crate::builtin::meta::PtrcallSignatureTuple>::ptrcall::<$Class>(
20+
<$Signature as $crate::builtin::meta::PtrcallSignatureTuple>::ptrcall(
2221
$instance_ptr,
2322
$args,
2423
$ret,
@@ -31,12 +30,11 @@ macro_rules! gdext_call_signature_method {
3130
(
3231
varcall,
3332
$Signature:ty,
34-
$Class:ty,
3533
$instance_ptr:ident, $args:ident, $ret:ident, $err:ident,
3634
$func:expr,
3735
$method_name:ident
3836
) => {
39-
<$Signature as $crate::builtin::meta::VarcallSignatureTuple>::varcall::<$Class>(
37+
<$Signature as $crate::builtin::meta::VarcallSignatureTuple>::varcall(
4038
$instance_ptr,
4139
$args,
4240
$ret,

godot-macros/src/method_registration/register_method.rs

+2-6
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ pub fn gdext_register_method(class_name: &Ident, method_signature: &TokenStream)
3131

3232
let wrapped_method = wrap_with_unpacked_params(class_name, &signature_info);
3333

34-
let varcall_func = get_varcall_func(class_name, method_name, &sig, &wrapped_method);
35-
let ptrcall_func = get_ptrcall_func(class_name, method_name, &sig, &wrapped_method);
34+
let varcall_func = get_varcall_func(method_name, &sig, &wrapped_method);
35+
let ptrcall_func = get_ptrcall_func(method_name, &sig, &wrapped_method);
3636

3737
quote! {
3838
{
@@ -72,7 +72,6 @@ pub fn gdext_register_method(class_name: &Ident, method_signature: &TokenStream)
7272
}
7373

7474
fn get_varcall_func(
75-
class_name: &Ident,
7675
method_name: &Ident,
7776
sig: &TokenStream,
7877
wrapped_method: &TokenStream,
@@ -93,7 +92,6 @@ fn get_varcall_func(
9392
godot::private::gdext_call_signature_method!(
9493
varcall,
9594
#sig,
96-
#class_name,
9795
instance_ptr,
9896
args,
9997
ret,
@@ -119,7 +117,6 @@ fn get_varcall_func(
119117
}
120118

121119
fn get_ptrcall_func(
122-
class_name: &Ident,
123120
method_name: &Ident,
124121
sig: &TokenStream,
125122
wrapped_method: &TokenStream,
@@ -138,7 +135,6 @@ fn get_ptrcall_func(
138135
godot::private::gdext_call_signature_method!(
139136
ptrcall,
140137
#sig,
141-
#class_name,
142138
instance_ptr,
143139
args,
144140
ret,

godot-macros/src/method_registration/virtual_method_callback.rs

-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ pub fn gdext_virtual_method_callback(
3636
godot::private::gdext_call_signature_method!(
3737
ptrcall,
3838
#sig,
39-
#class_name,
4039
instance_ptr,
4140
args,
4241
ret,

0 commit comments

Comments
 (0)