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

Incorrect slicing of functions ending with a noreturn call. #418

Open
lzaoral opened this issue Nov 23, 2021 · 1 comment
Open

Incorrect slicing of functions ending with a noreturn call. #418

lzaoral opened this issue Nov 23, 2021 · 1 comment
Labels

Comments

@lzaoral
Copy link
Contributor

lzaoral commented Nov 23, 2021

Consider the following C program

#include <assert.h>
#include <stdlib.h>

void test_assert(int arg) {
    assert(arg);
}

int main(void)
{
    test_assert(1);
    exit(0);
}

and slice it like this:

$ clang -O0 -c -emit-llvm test.c -o test.bc
$ llvm-slicer -c __assert_fail test.bc

Problem: The sliced bitcode now has a reachable unreachable instruction because test_assert will always return:

; Function Attrs: noinline nounwind optnone sspstrong uwtable
define dso_local void @test_assert(i32 %0) #0 !dbg !11 {
  %2 = alloca i32, align 4
  store i32 %0, i32* %2, align 4
  %3 = load i32, i32* %2, align 4, !dbg !15
  %4 = icmp ne i32 %3, 0, !dbg !15
  br i1 %4, label %safe_return, label %5, !dbg !18

5:                                                ; preds = %1
  call void @__assert_fail(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i64 0, i64 0), i8* getelementptr inbounds ([7 x i8], [7 x i8]* @.
str.1, i64 0, i64 0), i32 5, i8* getelementptr inbounds ([22 x i8], [22 x i8]* @__PRETTY_FUNCTION__.test_assert, i64 0, i64 0)) #2, !dbg !15
  unreachable, !dbg !15

safe_return:                                      ; preds = %1
  ret void
}

; Function Attrs: noreturn nounwind
declare void @__assert_fail(i8*, i8*, i32, i8*) #1

; Function Attrs: noinline nounwind optnone sspstrong uwtable
define dso_local i32 @main() #0 !dbg !19 {
  call void @test_assert(i32 1), !dbg !22
  unreachable, !dbg !23
}

EDIT: typo

@mchalupa mchalupa added the bug label Nov 23, 2021
@mchalupa mchalupa pinned this issue Nov 23, 2021
@lzaoral lzaoral changed the title Incorrect slicing of programs ending with an exit(3) call. Incorrect slicing of functions ending with an noreturn call. Dec 29, 2021
@lzaoral
Copy link
Contributor Author

lzaoral commented Dec 29, 2021

The issue is actually more complex than just error(3). The same applies to any function (e.g. not just main) that does not return and is decorated by _Noreturn function specifier from C11 (the same probably holds for __attribute__((noreturn)) as well). Note that such function will have the noreturn function attribute in LLVM.

#include <assert.h>
#include <stdlib.h>
#include <stdnoreturn.h>

void test_assert(int arg) {
    assert(arg);
}

noreturn void foo(void) {
    exit(0);
}

int main(void)
{
    test_assert(1);
    foo();
}

It's important to check bodies of all such functions though because as C11 says:

If the function declared _Noreturn returns, the behaviour is undefined.

So just replacing all undefined instructions in IR after calls of functions with noreturn attributes will hide this issue.

@lzaoral lzaoral changed the title Incorrect slicing of functions ending with an noreturn call. Incorrect slicing of functions ending with a noreturn call. Jun 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants