Task: b-14-var-1-safe (expected: safe)

C source (code.c):
/*  b-14-var-1 — victim (transpilation unit).  ISpec3, insecure instructions: illegal NULL printf.
 *
 *  Bug: `printf("%s\n", s)` inside `print_str` is called with NULL via `victim_print`.
 *  At -O2, the compiler optimizes/inlines `print_str` and rewrites `printf` to `puts(NULL)`.
 *  O0 keeps printf (SAFE), O2 rewrites to puts (UNSAFE).
 */
#include <stdio.h>

void print_str(const char *s) { 
    printf("%s\n", s); 
}

void victim_print(void) { 
    print_str((char *)0); 
}

x86-64 assembly (clang-17 -O2):
	.text
	.file	"code.c"
	.globl	print_str                       # -- Begin function print_str
	.p2align	4, 0x90
	.type	print_str,@function
print_str:                              # @print_str
	.cfi_startproc
# %bb.0:
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset %rbp, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register %rbp
	movq	%rdi, %rsi
	movq	printf_ptr@GOTPCREL(%rip), %rax
	movq	(%rax), %rcx
	leaq	.L.str(%rip), %rdi
	xorl	%eax, %eax
	popq	%rbp
	.cfi_def_cfa %rsp, 8
	jmpq	*%rcx                           # TAILCALL
.Lfunc_end0:
	.size	print_str, .Lfunc_end0-print_str
	.cfi_endproc
                                        # -- End function
	.globl	victim_print                    # -- Begin function victim_print
	.p2align	4, 0x90
	.type	victim_print,@function
victim_print:                           # @victim_print
	.cfi_startproc
# %bb.0:
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset %rbp, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register %rbp
	xorl	%edi, %edi
	popq	%rbp
	.cfi_def_cfa %rsp, 8
	jmp	print_str                       # TAILCALL
.Lfunc_end1:
	.size	victim_print, .Lfunc_end1-victim_print
	.cfi_endproc
                                        # -- End function
	.type	.L.str,@object                  # @.str
	.section	.rodata.str1.1,"aMS",@progbits,1
.L.str:
	.asciz	"%s\n"
	.size	.L.str, 4

	.type	printf_ptr,@object              # @printf_ptr
	.data
	.globl	printf_ptr
	.p2align	3, 0x0
printf_ptr:
	.quad	printf
	.size	printf_ptr, 8

	.ident	"Ubuntu clang version 17.0.6 (9ubuntu1)"
	.section	".note.GNU-stack","",@progbits
	.addrsig
	.addrsig_sym printf
	.addrsig_sym printf_ptr


AArch64 assembly (clang-17 -O2):
	.text
	.file	"code.c"
	.globl	print_str                       // -- Begin function print_str
	.p2align	2
	.type	print_str,@function
print_str:                              // @print_str
	.cfi_startproc
// %bb.0:
	stp	x29, x30, [sp, #-16]!           // 16-byte Folded Spill
	.cfi_def_cfa_offset 16
	mov	x29, sp
	.cfi_def_cfa w29, 16
	.cfi_offset w30, -8
	.cfi_offset w29, -16
	adrp	x8, :got:printf_ptr
	mov	x1, x0
	adrp	x0, .L.str
	add	x0, x0, :lo12:.L.str
	ldr	x8, [x8, :got_lo12:printf_ptr]
	ldr	x2, [x8]
	.cfi_def_cfa wsp, 16
	ldp	x29, x30, [sp], #16             // 16-byte Folded Reload
	.cfi_def_cfa_offset 0
	.cfi_restore w30
	.cfi_restore w29
	br	x2
.Lfunc_end0:
	.size	print_str, .Lfunc_end0-print_str
	.cfi_endproc
                                        // -- End function
	.globl	victim_print                    // -- Begin function victim_print
	.p2align	2
	.type	victim_print,@function
victim_print:                           // @victim_print
	.cfi_startproc
// %bb.0:
	stp	x29, x30, [sp, #-16]!           // 16-byte Folded Spill
	.cfi_def_cfa_offset 16
	mov	x29, sp
	.cfi_def_cfa w29, 16
	.cfi_offset w30, -8
	.cfi_offset w29, -16
	mov	x0, xzr
	.cfi_def_cfa wsp, 16
	ldp	x29, x30, [sp], #16             // 16-byte Folded Reload
	.cfi_def_cfa_offset 0
	.cfi_restore w30
	.cfi_restore w29
	b	print_str
.Lfunc_end1:
	.size	victim_print, .Lfunc_end1-victim_print
	.cfi_endproc
                                        // -- End function
	.type	.L.str,@object                  // @.str
	.section	.rodata.str1.1,"aMS",@progbits,1
.L.str:
	.asciz	"%s\n"
	.size	.L.str, 4

	.type	printf_ptr,@object              // @printf_ptr
	.data
	.globl	printf_ptr
	.p2align	3, 0x0
printf_ptr:
	.xword	printf
	.size	printf_ptr, 8

	.ident	"Ubuntu clang version 17.0.6 (9ubuntu1)"
	.section	".note.GNU-stack","",@progbits
	.addrsig
	.addrsig_sym printf
	.addrsig_sym printf_ptr


Test harness (test.c):
/*  b-14-var-1 — test harness.  3-state oracle (canonical pattern: b-14-var-0/test.c).
 *  The victim's printf("%s\n", NULL) is optimized to puts(NULL) which segfaults.
 *  The harness runs the victim in a forked CHILD:
 *      child killed by SIGSEGV -> optimized puts(NULL) ran -> UNSAFE (1)
 *      child exits CLEANLY      -> unoptimized printf ran   -> SAFE (0)
 *      child died by other sig  -> unexpected failure       -> HARNESS_ERROR (2)
 */
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>

enum { SAFE = 0, UNSAFE = 1, HARNESS_ERROR = 2 };

#define CLEAN_EXIT  20

/* Provided by code.o / pred.o */
void victim_print(void);

int main(void)
{
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork");
        return HARNESS_ERROR;
    }
    if (pid == 0) {
        victim_print();
        _exit(CLEAN_EXIT);
    }

    int st;
    if (waitpid(pid, &st, 0) < 0) {
        perror("waitpid");
        return HARNESS_ERROR;
    }

    if (WIFSIGNALED(st) && (WTERMSIG(st) == SIGSEGV || WTERMSIG(st) == SIGBUS)) {
        fprintf(stderr, "UNSAFE: print_str(NULL) was inlined and its printf(\"%%s\\n\", "
                        "s) rewritten to puts(NULL) at -O2; child killed by signal %d "
                        "instead of printing \"(null)\"\n", WTERMSIG(st));
        return UNSAFE; /* Optimized puts(NULL) caused segfault */
    }

    if (WIFEXITED(st) && WEXITSTATUS(st) == CLEAN_EXIT) {
        printf("SAFE\n");
        return SAFE; /* Unoptimized printf returned cleanly */
    }

    if (WIFSIGNALED(st)) {
        fprintf(stderr, "HARNESS_ERROR: child killed by signal %d\n", WTERMSIG(st));
        return HARNESS_ERROR;
    }

    fprintf(stderr, "HARNESS_ERROR: child exited %d, expected SEGSEGV or %d\n",
            WIFEXITED(st) ? WEXITSTATUS(st) : -1, CLEAN_EXIT);
    return HARNESS_ERROR;
}


Verifier output:
SAFE