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

C source (code.c):
/*  b-21-var-1 — victim (transpilation unit).  OSpec1 / DSE-memset, stack variant.
 *
 *  Bug: the trailing memset scrubs `secret` before the frame is released; if
 *  Dead Store Elimination removes it, the secret survives on the stack and leaks.
 *  O0 keeps it (SAFE), O2 drops it (UNSAFE).
 *  Note: returning a value (not void) keeps the final printf from becoming a tail
 *  call that would tear down the frame before the harness can read it.
 */
#include <stdio.h>
#include <string.h>

static const char *const volatile secret_src = "MY_SECRET_PASSWORD";

/* Arbitrary liveness handshake shared with test.c. Not secret-derived — any
 * agreed constant works (e.g. 100); 0xB21 just nods to the benchmark id. */
#define VICTIM_RAN 100

int process_secret(void)
{
    char secret[64];

    strncpy(secret, (const char *)secret_src, sizeof(secret) - 1);
    secret[sizeof(secret) - 1] = '\0';

    printf("Secret length: %d\n", (int)strlen(secret));

    memset(secret, 0, sizeof(secret));   /* scrub — DSE target */
    return VICTIM_RAN; /*This to check the behaviour*/
}


x86-64 assembly (clang-17 -O2):
	.text
	.file	"code.c"
	.globl	process_secret                  # -- Begin function process_secret
	.p2align	4, 0x90
	.type	process_secret,@function
process_secret:                         # @process_secret
	.cfi_startproc
# %bb.0:
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset %rbp, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register %rbp
	pushq	%rbx
	subq	$72, %rsp
	.cfi_offset %rbx, -24
	movq	secret_src(%rip), %rsi
	leaq	-80(%rbp), %rbx
	movl	$63, %edx
	movq	%rbx, %rdi
	callq	strncpy@PLT
	movb	$0, -17(%rbp)
	movq	%rbx, %rdi
	callq	strlen@PLT
	leaq	.L.str(%rip), %rdi
	movl	%eax, %esi
	xorl	%eax, %eax
	callq	printf@PLT
	movb	$0, -80(%rbp)
	movb	$0, -79(%rbp)
	movb	$0, -78(%rbp)
	movb	$0, -77(%rbp)
	movb	$0, -76(%rbp)
	movb	$0, -75(%rbp)
	movb	$0, -74(%rbp)
	movb	$0, -73(%rbp)
	movb	$0, -72(%rbp)
	movb	$0, -71(%rbp)
	movb	$0, -70(%rbp)
	movb	$0, -69(%rbp)
	movb	$0, -68(%rbp)
	movb	$0, -67(%rbp)
	movb	$0, -66(%rbp)
	movb	$0, -65(%rbp)
	movb	$0, -64(%rbp)
	movb	$0, -63(%rbp)
	movb	$0, -62(%rbp)
	movb	$0, -61(%rbp)
	movb	$0, -60(%rbp)
	movb	$0, -59(%rbp)
	movb	$0, -58(%rbp)
	movb	$0, -57(%rbp)
	movb	$0, -56(%rbp)
	movb	$0, -55(%rbp)
	movb	$0, -54(%rbp)
	movb	$0, -53(%rbp)
	movb	$0, -52(%rbp)
	movb	$0, -51(%rbp)
	movb	$0, -50(%rbp)
	movb	$0, -49(%rbp)
	movb	$0, -48(%rbp)
	movb	$0, -47(%rbp)
	movb	$0, -46(%rbp)
	movb	$0, -45(%rbp)
	movb	$0, -44(%rbp)
	movb	$0, -43(%rbp)
	movb	$0, -42(%rbp)
	movb	$0, -41(%rbp)
	movb	$0, -40(%rbp)
	movb	$0, -39(%rbp)
	movb	$0, -38(%rbp)
	movb	$0, -37(%rbp)
	movb	$0, -36(%rbp)
	movb	$0, -35(%rbp)
	movb	$0, -34(%rbp)
	movb	$0, -33(%rbp)
	movb	$0, -32(%rbp)
	movb	$0, -31(%rbp)
	movb	$0, -30(%rbp)
	movb	$0, -29(%rbp)
	movb	$0, -28(%rbp)
	movb	$0, -27(%rbp)
	movb	$0, -26(%rbp)
	movb	$0, -25(%rbp)
	movb	$0, -24(%rbp)
	movb	$0, -23(%rbp)
	movb	$0, -22(%rbp)
	movb	$0, -21(%rbp)
	movb	$0, -20(%rbp)
	movb	$0, -19(%rbp)
	movb	$0, -18(%rbp)
	movb	$0, -17(%rbp)
	movl	$100, %eax
	addq	$72, %rsp
	popq	%rbx
	popq	%rbp
	.cfi_def_cfa %rsp, 8
	retq
.Lfunc_end0:
	.size	process_secret, .Lfunc_end0-process_secret
	.cfi_endproc
                                        # -- End function
	.type	secret_src,@object              # @secret_src
	.section	.data.rel.ro,"aw",@progbits
	.p2align	3, 0x0
secret_src:
	.quad	.L.str.1
	.size	secret_src, 8

	.type	.L.str,@object                  # @.str
	.section	.rodata.str1.1,"aMS",@progbits,1
.L.str:
	.asciz	"Secret length: %d\n"
	.size	.L.str, 19

	.type	.L.str.1,@object                # @.str.1
.L.str.1:
	.asciz	"MY_SECRET_PASSWORD"
	.size	.L.str.1, 19

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


AArch64 assembly (clang-17 -O2):
	.text
	.file	"code.c"
	.globl	process_secret                  // -- Begin function process_secret
	.p2align	2
	.type	process_secret,@function
process_secret:                         // @process_secret
	.cfi_startproc
// %bb.0:
	sub	sp, sp, #80
	.cfi_def_cfa_offset 80
	stp	x29, x30, [sp, #64]             // 16-byte Folded Spill
	add	x29, sp, #64
	.cfi_def_cfa w29, 16
	.cfi_offset w30, -8
	.cfi_offset w29, -16
	adrp	x8, secret_src
	mov	x0, sp
	mov	w2, #63                         // =0x3f
	ldr	x1, [x8, :lo12:secret_src]
	bl	strncpy
	mov	x0, sp
	strb	wzr, [sp, #63]
	bl	strlen
	mov	x1, x0
	adrp	x0, .L.str
	add	x0, x0, :lo12:.L.str
                                        // kill: def $w1 killed $w1 killed $x1
	bl	printf
	strb	wzr, [sp]
	mov	w0, #100                        // =0x64
	strb	wzr, [sp, #1]
	strb	wzr, [sp, #2]
	strb	wzr, [sp, #3]
	strb	wzr, [sp, #4]
	strb	wzr, [sp, #5]
	strb	wzr, [sp, #6]
	strb	wzr, [sp, #7]
	strb	wzr, [sp, #8]
	strb	wzr, [sp, #9]
	strb	wzr, [sp, #10]
	strb	wzr, [sp, #11]
	strb	wzr, [sp, #12]
	strb	wzr, [sp, #13]
	strb	wzr, [sp, #14]
	strb	wzr, [sp, #15]
	strb	wzr, [sp, #16]
	strb	wzr, [sp, #17]
	strb	wzr, [sp, #18]
	strb	wzr, [sp, #19]
	strb	wzr, [sp, #20]
	strb	wzr, [sp, #21]
	strb	wzr, [sp, #22]
	strb	wzr, [sp, #23]
	strb	wzr, [sp, #24]
	strb	wzr, [sp, #25]
	strb	wzr, [sp, #26]
	strb	wzr, [sp, #27]
	strb	wzr, [sp, #28]
	strb	wzr, [sp, #29]
	strb	wzr, [sp, #30]
	strb	wzr, [sp, #31]
	strb	wzr, [sp, #32]
	strb	wzr, [sp, #33]
	strb	wzr, [sp, #34]
	strb	wzr, [sp, #35]
	strb	wzr, [sp, #36]
	strb	wzr, [sp, #37]
	strb	wzr, [sp, #38]
	strb	wzr, [sp, #39]
	strb	wzr, [sp, #40]
	strb	wzr, [sp, #41]
	strb	wzr, [sp, #42]
	strb	wzr, [sp, #43]
	strb	wzr, [sp, #44]
	strb	wzr, [sp, #45]
	strb	wzr, [sp, #46]
	strb	wzr, [sp, #47]
	strb	wzr, [sp, #48]
	strb	wzr, [sp, #49]
	strb	wzr, [sp, #50]
	strb	wzr, [sp, #51]
	strb	wzr, [sp, #52]
	strb	wzr, [sp, #53]
	strb	wzr, [sp, #54]
	strb	wzr, [sp, #55]
	strb	wzr, [sp, #56]
	strb	wzr, [sp, #57]
	strb	wzr, [sp, #58]
	strb	wzr, [sp, #59]
	strb	wzr, [sp, #60]
	strb	wzr, [sp, #61]
	strb	wzr, [sp, #62]
	strb	wzr, [sp, #63]
	.cfi_def_cfa wsp, 80
	ldp	x29, x30, [sp, #64]             // 16-byte Folded Reload
	add	sp, sp, #80
	.cfi_def_cfa_offset 0
	.cfi_restore w30
	.cfi_restore w29
	ret
.Lfunc_end0:
	.size	process_secret, .Lfunc_end0-process_secret
	.cfi_endproc
                                        // -- End function
	.type	secret_src,@object              // @secret_src
	.section	.data.rel.ro,"aw",@progbits
	.p2align	3, 0x0
secret_src:
	.xword	.L.str.1
	.size	secret_src, 8

	.type	.L.str,@object                  // @.str
	.section	.rodata.str1.1,"aMS",@progbits,1
.L.str:
	.asciz	"Secret length: %d\n"
	.size	.L.str, 19

	.type	.L.str.1,@object                // @.str.1
.L.str.1:
	.asciz	"MY_SECRET_PASSWORD"
	.size	.L.str.1, 19

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


Test harness (test.c):
/*  b-21-var-1 — test harness.  Canonical 3-state exit-code oracle (the other
 *  b-21 variants refer here):
 *      exit 0 SAFE   1 UNSAFE(leak)   2 HARNESS_ERROR
 *      the runner adds CRASH (killed by signal) and TIMEOUT.
 *  Not assert-based, so a leak (clean exit 1) stays distinct from a crash.
 *  Liveness: the victim returns VICTIM_RAN; any other value -> HARNESS_ERROR
 *  (so a broken victim can't be mistaken for SAFE).
 *
 *  Special: attacker() is noinline with a wide `volatile` array so it reuses the
 *  victim's freed stack frame; find() is a generic sliding search because the
 *  leaked bytes land at a different offset per arch. SECRET is held here only to
 *  recognise a leak — it is never passed into the victim.
 */
#include <stdio.h>
#include <string.h>

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

#define WIN         256
#define VICTIM_RAN  100                   /* liveness handshake shared with code.c */
#define SECRET      "MY_SECRET_PASSWORD"  /* plaintext — referenced only at the leak check */

/* Provided by the transpiled object (pred.o / code.o) at link time. */
int process_secret(void);

static unsigned char g_snapshot[WIN];

__attribute__((noinline))
static void attacker(void)
{
    volatile char leak[WIN];                 /* reuses the victim's freed frame */
    for (int i = 0; i < WIN; i++)
        g_snapshot[i] = (unsigned char)leak[i];
}

/* Generic, secret-agnostic byte search: return the offset of `needle` inside
 * `hay`, or -1 if absent. It knows nothing about "the secret" — the caller
 * decides what to look for. (A sliding search is required because the leaked
 * bytes land at a different stack offset on each architecture.) */
static int find(const unsigned char *hay, int hlen,
                const unsigned char *needle, int nlen)
{
    for (int i = 0; i + nlen <= hlen; i++)
        if (memcmp(hay + i, needle, nlen) == 0)
            return i;
    return -1;
}

int main(void)
{
    int token = process_secret();          /* liveness handshake sentinel */
    attacker();

    /* State 3: victim ran but did not behave as the transpilation unit should. */
    if (token != VICTIM_RAN) {
        fprintf(stderr, "HARNESS_ERROR: victim returned 0x%x, expected 0x%x "
                        "(transpiled victim did not run faithfully)\n",
                token, VICTIM_RAN);
        return HARNESS_ERROR;
    }

    /* States 1 / 2: victim ran faithfully — now the only question is the leak.
     * SECRET is used ONLY here, to decide whether the captured stack leaked it. */
    int secret_len = (int)strlen(SECRET);
    int off = find(g_snapshot, WIN, (const unsigned char *)SECRET, secret_len);
    if (off >= 0) {
        /* Print the secret we RECOVERED from the reused stack frame. */
        fprintf(stderr, "UNSAFE: secret leaked — scrub was eliminated; "
                        "recovered from stack: \"");
        for (int i = 0; i < secret_len; i++)
            fputc(g_snapshot[off + i], stderr);
        fprintf(stderr, "\"\n");
        return UNSAFE;
    }

    printf("SAFE\n");
    return SAFE;
}


Verifier output:
Secret length: 18
SAFE