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

Add ASAN #1394

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

Add ASAN #1394

wants to merge 1 commit into from

Conversation

jakub-gonet
Copy link
Contributor

It was immensely helpful when developing so I'm opting to run it by default. Open to discussion, though.

On MacOS running an AVM shows a warning "malloc: nano zone abandoned due to inability to reserve vm space.". To avoid this warning, set MallocNanoZone=0 env variable before running the VM.

Also adds debug symbols when compiling with Clang.


These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

On MacOS running an AVM shows a warning "malloc: nano zone abandoned due to inability to reserve vm space.".
To avoid this warning, set MallocNanoZone=0 env variable.

Also adds debug symbols when compiling with Clang.

Signed-off-by: Jakub Gonet <[email protected]>
@pguyot
Copy link
Collaborator

pguyot commented Dec 11, 2024

I do use ASAN in a stash as well.
We need to fix the leak found in packbeam.c though (file_data isn't freed when iterating on args).

@jakub-gonet
Copy link
Contributor Author

...or we can ignore for the time being, running ASAN_OPTIONS=detect_leaks=0 packBeam .... I checked implementation and the most non-intrusive way would be to use atexit with global variable. Good alternative (IMO) is to bubble up errors and exit in one point in program.

@@ -35,6 +35,7 @@ option(AVM_RELEASE "Build an AtomVM release" OFF)
option(AVM_CREATE_STACKTRACES "Create stacktraces" ON)
option(AVM_BUILD_RUNTIME_ONLY "Only build the AtomVM runtime" OFF)
option(COVERAGE "Build for code coverage" OFF)
option(ENABLE_ASAN "Use Address Sanitizer" ON)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure if this should be the default: it is very useful during development, but it should be off during release.
Also I'm not sure how interacts with platforms that do not properly support -fsanitize=address such as ESP32.

So maybe it should be manually enabled (and we might suggest it in our documentation) and of course we should add a job in our CI that enables it. After all what really matter is not merging code that fails those checks.

I think we should have jobs and options for undefined behavior sanitizer, thread sanitizer and memory sanitizer and they should be part of our CI as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. I don't have means to test embedded platforms so most of my testing comes from unix and WASM port.

What do you think about adding

set(SANITIZER none) # available values: address, memory, undefined, none

and corresponding ifs in libAtomVM CMakeList?

Is there any way to create local CMakeLists or you need to use env variables when running CMake to set those options?

@@ -120,7 +120,7 @@ target_compile_features(libAtomVM PUBLIC c_std_11)
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
target_compile_options(libAtomVM PUBLIC -Wall ${MAYBE_PEDANTIC_FLAG} ${MAYBE_WERROR_FLAG} -Wextra -ggdb -Werror=incompatible-pointer-types)
elseif (CMAKE_C_COMPILER_ID MATCHES "Clang")
target_compile_options(libAtomVM PUBLIC -Wall --extra-warnings -Werror=incompatible-pointer-types ${MAYBE_WERROR_FLAG})
target_compile_options(libAtomVM PUBLIC -Wall --extra-warnings -Werror=incompatible-pointer-types -g ${MAYBE_WERROR_FLAG})
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that -g is already the default when building with cmake in Debug mode.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is build mode dictated by DEBUG env variable? I couldn't find reference to debug/release in the code.

@@ -132,6 +132,11 @@ if (ADVANCED_TRACING)
target_compile_definitions(libAtomVM PUBLIC ENABLE_ADVANCED_TRACE)
endif()

if(ENABLE_ASAN)
target_compile_options(libAtomVM PUBLIC -fsanitize=address -fno-omit-frame-pointer)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is -fno-omit-frame-pointer cross platform? I remember that was something quite common on i386 that has few available registers, but I think it is not the default on several platforms (and maybe it is not even available as an option on other archs).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. Logically, omitting frame pointer is an optimization so every platform should support that (making one less available register if disabled) but from reading GCC docs it's noop on archs that don't benefit from it.

https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Optimize-Options.html
look for -fomit-frame-pointer

@bettio
Copy link
Collaborator

bettio commented Dec 13, 2024

...or we can ignore for the time being, running ASAN_OPTIONS=detect_leaks=0 packBeam .... I checked implementation and the most non-intrusive way would be to use atexit with global variable. Good alternative (IMO) is to bubble up errors and exit in one point in program.

I'm not a huge fan of this workaround, I would rather go for the clean way. By the way, isn't adding free() on that loop an option?

@jakub-gonet
Copy link
Contributor Author

I'm not a huge fan of this workaround, I would rather go for the clean way. By the way, isn't adding free() on that loop an option?

From reading the code, we malloc file_data in do_pack which uses assert_fread to exit if it can't read specified number of bytes. This means we need to change assert_fread to either know about unfreed allocations or not to exit.
That's why I suggested atexit.

ASAN does report it as a violation even at exit (typically OS collects unfreed memory by then) and I didn't find any way to skip reporting exit-freed allocations.
I also looked into adding ASAN dynamically but I couldn't get it to work on MacOS (I remember reading that when comes to GCC and Clang one links it statically, one dynamically and the other configuration for both is less tested. This means it's better to use the default linking strategy).

@bettio
Copy link
Collaborator

bettio commented Dec 17, 2024

I tried [1] but then it started crashing on my PC with sanitize=memory. Also I feel like we are leaking file descriptors too.

[1]

--- a/tools/packbeam/packbeam.c
+++ b/tools/packbeam/packbeam.c
@@ -271,6 +271,8 @@ static int do_pack(int argc, char **argv, int is_archive, bool include_lines)
             char *filename = basename(argv[i]);
             pack_beam_file(pack, file_data, file_size, filename, !is_archive && i == 1, include_lines);
         }
+
+        free(file_data);
     }

     add_module_header(pack, "end", END_OF_FILE);

@jakub-gonet
Copy link
Contributor Author

jakub-gonet commented Dec 17, 2024

I spent ~2h on this draft, this is how I'd rewrite the file (I didn't run it at all, so expect errors if you want to work on it further). It isn't the most readable code but I'm not a C expert.

Bubbling the errors up is really painful. Related: what's your opinion on Zig? :P

We leak descriptors and memory mostly when checking if files are AVM or BEAM ones. We also do a bit of unnecessary full file reads. I think it would be nice to mmap all of them instead of doing a mix of mmap and fopens.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants