Task: b-21-var-2-unsafe (expected: unsafe)

C source (code.c):
/*  b-21-var-2 — victim (transpilation unit).  OSpec1 / DSE-memset, heap struct.
 *
 *  Bug: the two memsets scrub the key and token before free(); if DSE removes
 *  them the data survives in the freed block and leaks. O0 keeps them (SAFE),
 *  O2 drops them (UNSAFE).
 *  Note: aes_key sits at struct offset 36 — past the ~16 bytes glibc's freelist
 *  bookkeeping overwrites in a freed chunk (token at offset 0 is clobbered).
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define KEY_SIZE   16
#define TOKEN_SIZE 32

struct session {
    char token[TOKEN_SIZE];
    int  user_id;
    char aes_key[KEY_SIZE];
};

static const char *const volatile key_src   = "SECRETKEY1234567";
static const char *const volatile token_src = "TOKEN_ABCDEFGHIJKLMNOPQRSTUVWXY";  /* 31 chars + NUL = 32 */

/* Arbitrary liveness handshake shared with test.c. Not secret-derived. */
#define VICTIM_RAN 100

int victim(void)
{
    struct session *s = malloc(sizeof(struct session));
    if (!s)
        return 0;

    s->user_id = 42;
    memcpy(s->aes_key, (const char *)key_src,   KEY_SIZE);
    memcpy(s->token,   (const char *)token_src, TOKEN_SIZE);

    /* Force both fields to actually land in the heap block. */
    int cksum = 0;
    for (int i = 0; i < KEY_SIZE; i++)   cksum += (unsigned char)s->aes_key[i];
    for (int i = 0; i < TOKEN_SIZE; i++) cksum += (unsigned char)s->token[i];
    printf("Session for user %d ready (cksum=%d)\n", s->user_id, cksum);

    /* Scrub both sensitive fields before free — DSE targets. */
    memset(s->aes_key, 0, KEY_SIZE);
    memset(s->token,   0, TOKEN_SIZE);
    free(s);
    return VICTIM_RAN;      /* handshake: reveals nothing about the secret */
}


x86-64 assembly (clang-17 -O2):
	.text
	.file	"code.c"
	.globl	victim                          # -- Begin function victim
	.p2align	4, 0x90
	.type	victim,@function
victim:                                 # @victim
	.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
	pushq	%rax
	.cfi_offset %rbx, -24
	movl	$52, %edi
	callq	malloc@PLT
	testq	%rax, %rax
	je	.LBB0_1
# %bb.2:                                # %.preheader.preheader
	movq	%rax, %rbx
	movl	$42, 32(%rax)
	movq	key_src(%rip), %rax
	movups	(%rax), %xmm0
	movups	%xmm0, 36(%rbx)
	movq	token_src(%rip), %rax
	movdqu	(%rax), %xmm0
	movdqu	16(%rax), %xmm1
	movdqu	%xmm1, 16(%rbx)
	movdqu	%xmm0, (%rbx)
	movdqu	36(%rbx), %xmm2
	pxor	%xmm3, %xmm3
	movdqa	%xmm2, %xmm4
	punpckhbw	%xmm3, %xmm4            # xmm4 = xmm4[8],xmm3[8],xmm4[9],xmm3[9],xmm4[10],xmm3[10],xmm4[11],xmm3[11],xmm4[12],xmm3[12],xmm4[13],xmm3[13],xmm4[14],xmm3[14],xmm4[15],xmm3[15]
	punpcklbw	%xmm3, %xmm2            # xmm2 = xmm2[0],xmm3[0],xmm2[1],xmm3[1],xmm2[2],xmm3[2],xmm2[3],xmm3[3],xmm2[4],xmm3[4],xmm2[5],xmm3[5],xmm2[6],xmm3[6],xmm2[7],xmm3[7]
	pshuflw	$225, %xmm2, %xmm2              # xmm2 = xmm2[1,0,2,3,4,5,6,7]
	packuswb	%xmm4, %xmm2
	psadbw	%xmm3, %xmm2
	psadbw	%xmm3, %xmm0
	paddq	%xmm2, %xmm0
	pshufd	$238, %xmm0, %xmm2              # xmm2 = xmm0[2,3,2,3]
	paddq	%xmm0, %xmm2
	movd	%xmm2, %eax
	psadbw	%xmm3, %xmm1
	pshufd	$238, %xmm1, %xmm0              # xmm0 = xmm1[2,3,2,3]
	paddq	%xmm1, %xmm0
	movd	%xmm0, %edx
	addl	%eax, %edx
	leaq	.L.str(%rip), %rdi
	movl	$42, %esi
	xorl	%eax, %eax
	callq	printf@PLT
	movq	%rbx, %rdi
	callq	free@PLT
	movl	$100, %eax
	jmp	.LBB0_3
.LBB0_1:
	xorl	%eax, %eax
.LBB0_3:
	addq	$8, %rsp
	popq	%rbx
	popq	%rbp
	.cfi_def_cfa %rsp, 8
	retq
.Lfunc_end0:
	.size	victim, .Lfunc_end0-victim
	.cfi_endproc
                                        # -- End function
	.type	key_src,@object                 # @key_src
	.section	.data.rel.ro,"aw",@progbits
	.p2align	3, 0x0
key_src:
	.quad	.L.str.1
	.size	key_src, 8

	.type	token_src,@object               # @token_src
	.p2align	3, 0x0
token_src:
	.quad	.L.str.2
	.size	token_src, 8

	.type	.L.str,@object                  # @.str
	.section	.rodata.str1.1,"aMS",@progbits,1
.L.str:
	.asciz	"Session for user %d ready (cksum=%d)\n"
	.size	.L.str, 38

	.type	.L.str.1,@object                # @.str.1
.L.str.1:
	.asciz	"SECRETKEY1234567"
	.size	.L.str.1, 17

	.type	.L.str.2,@object                # @.str.2
.L.str.2:
	.asciz	"TOKEN_ABCDEFGHIJKLMNOPQRSTUVWXY"
	.size	.L.str.2, 32

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


AArch64 assembly (clang-17 -O2):
	.text
	.file	"code.c"
	.section	.rodata.cst8,"aM",@progbits,8
	.p2align	3, 0x0                          // -- Begin function victim
.LCPI0_0:
	.byte	1                               // 0x1
	.byte	0                               // 0x0
	.byte	2                               // 0x2
	.byte	3                               // 0x3
	.byte	4                               // 0x4
	.byte	5                               // 0x5
	.byte	6                               // 0x6
	.byte	7                               // 0x7
	.text
	.globl	victim
	.p2align	2
	.type	victim,@function
victim:                                 // @victim
	.cfi_startproc
// %bb.0:
	stp	x29, x30, [sp, #-32]!           // 16-byte Folded Spill
	.cfi_def_cfa_offset 32
	str	x19, [sp, #16]                  // 8-byte Folded Spill
	mov	x29, sp
	.cfi_def_cfa w29, 32
	.cfi_offset w19, -16
	.cfi_offset w30, -24
	.cfi_offset w29, -32
	mov	w0, #52                         // =0x34
	bl	malloc
	cbz	x0, .LBB0_2
// %bb.1:                               // %.preheader.preheader
	mov	w8, #42                         // =0x2a
	adrp	x9, key_src
	mov	x19, x0
	mov	w1, #42                         // =0x2a
	str	w8, [x0, #32]
	ldr	x8, [x9, :lo12:key_src]
	adrp	x9, .LCPI0_0
	ldr	q0, [x8]
	adrp	x8, token_src
	ldr	d4, [x9, :lo12:.LCPI0_0]
	stur	q0, [x0, #36]
	ldr	x8, [x8, :lo12:token_src]
	ldur	q0, [x0, #36]
	ldp	q2, q3, [x8]
	mov	v1.16b, v0.16b
	mov	v1.d[1], v0.d[0]
	ushll2	v0.8h, v0.16b, #0
	stp	q2, q3, [x0]
	tbl	v1.8b, { v1.16b }, v4.8b
	ldp	q2, q3, [x0]
	adrp	x0, .L.str
	add	x0, x0, :lo12:.L.str
	ushll	v1.8h, v1.8b, #0
	ushll	v4.8h, v2.8b, #0
	ushll2	v2.8h, v2.16b, #0
	ushll2	v5.8h, v3.16b, #0
	ushll	v3.8h, v3.8b, #0
	uaddl2	v6.4s, v0.8h, v2.8h
	uaddl	v0.4s, v0.4h, v2.4h
	uaddl2	v7.4s, v1.8h, v4.8h
	uaddl	v1.4s, v1.4h, v4.4h
	uaddl2	v2.4s, v3.8h, v5.8h
	uaddl	v3.4s, v3.4h, v5.4h
	add	v4.4s, v7.4s, v6.4s
	add	v0.4s, v1.4s, v0.4s
	add	v1.4s, v3.4s, v2.4s
	add	v0.4s, v0.4s, v4.4s
	add	v0.4s, v0.4s, v1.4s
	addv	s0, v0.4s
	fmov	w2, s0
	bl	printf
	mov	x0, x19
	bl	free
	mov	w0, #100                        // =0x64
.LBB0_2:
	.cfi_def_cfa wsp, 32
	ldr	x19, [sp, #16]                  // 8-byte Folded Reload
	ldp	x29, x30, [sp], #32             // 16-byte Folded Reload
	.cfi_def_cfa_offset 0
	.cfi_restore w19
	.cfi_restore w30
	.cfi_restore w29
	ret
.Lfunc_end0:
	.size	victim, .Lfunc_end0-victim
	.cfi_endproc
                                        // -- End function
	.type	key_src,@object                 // @key_src
	.section	.data.rel.ro,"aw",@progbits
	.p2align	3, 0x0
key_src:
	.xword	.L.str.1
	.size	key_src, 8

	.type	token_src,@object               // @token_src
	.p2align	3, 0x0
token_src:
	.xword	.L.str.2
	.size	token_src, 8

	.type	.L.str,@object                  // @.str
	.section	.rodata.str1.1,"aMS",@progbits,1
.L.str:
	.asciz	"Session for user %d ready (cksum=%d)\n"
	.size	.L.str, 38

	.type	.L.str.1,@object                // @.str.1
.L.str.1:
	.asciz	"SECRETKEY1234567"
	.size	.L.str.1, 17

	.type	.L.str.2,@object                // @.str.2
.L.str.2:
	.asciz	"TOKEN_ABCDEFGHIJKLMNOPQRSTUVWXY"
	.size	.L.str.2, 32

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


Test harness (test.c):
/*  b-21-var-2 — test harness.  3-state oracle, see b-21-var-1/test.c.
 *  Special: struct-sized heap reuse — the local `struct session` mirrors code.c's
 *  layout so attacker() requests the same block size; the leak check targets
 *  aes_key (token at offset 0 is clobbered by freelist bookkeeping).
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

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

#define KEY_SIZE   16
#define TOKEN_SIZE 32

/* Must match code.c's layout so the attacker requests the same block size. */
struct session {
    char token[TOKEN_SIZE];
    int  user_id;
    char aes_key[KEY_SIZE];
};

#define BLOCK       ((int)sizeof(struct session))
#define VICTIM_RAN  100                 /* liveness handshake shared with code.c */
#define SECRET      "SECRETKEY1234567"  /* the aes_key — referenced only at the leak check */

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

static unsigned char g_snapshot[sizeof(struct session)];

/* Reuse the victim's just-freed struct block: glibc hands the freed same-size
 * block back to the very next malloc(). No allocation may happen between the
 * victim's free() and this malloc() — main() calls nothing between them. */
__attribute__((noinline))
static void attacker(void)
{
    volatile char *leak = malloc(sizeof(struct session));
    if (!leak)
        return;
    for (int i = 0; i < BLOCK; i++)
        g_snapshot[i] = (unsigned char)leak[i];
    free((void *)leak);
}

/* Generic, secret-agnostic byte search: offset of `needle` in `hay`, or -1. */
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 = victim();      /* liveness handshake sentinel */
    attacker();

    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;
    }

    /* SECRET is used ONLY here, to decide whether the key leaked. */
    int secret_len = (int)strlen(SECRET);
    int off = find(g_snapshot, BLOCK, (const unsigned char *)SECRET, secret_len);
    if (off >= 0) {
        fprintf(stderr, "UNSAFE: key leaked — scrub was eliminated; "
                        "recovered from heap: \"");
        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:
UNSAFE: key leaked — scrub was eliminated; recovered from heap: "SECRETKEY1234567"
Session for user 42 ready (cksum=3456)