Skip to content

[herd][AArch64] Fix GCS fault propagation - #1950

Draft
murzinv wants to merge 3 commits into
herd:masterfrom
murzinv:fix-gcs
Draft

[herd][AArch64] Fix GCS fault propagation#1950
murzinv wants to merge 3 commits into
herd:masterfrom
murzinv:fix-gcs

Conversation

@murzinv

@murzinv murzinv commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Opening it as a draft since:

  • Changes are AI assisted
  • Have not got time to check graphs

murzinv added 3 commits August 3, 2026 16:28
The AArch64 CAS monad helpers currently force the write_rs callback
and the overall operation to return unit. This prevents callers whose
register-update step determines instruction control flow from
propagating that result; in particular, a GCS CAS must retain either
its normal Next result or a Fault result instead of discarding it and
advancing unconditionally.

Generalise only the callback result from unit to an arbitrary type and
carry that value through the existing event structure. The generated
events, constraints, dependencies, and all other callbacks are
unchanged. Existing callers continue to instantiate the result as
unit, making this a backward-compatible and safe relaxation of the
helper interface.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
The generic lift_memop wrapper always discards the memory operation
result and returns B.Next. That is correct for ordinary loads and
stores, but not for GCS operations whose memory callbacks decide
control flow. It caused GCS faults to be replaced by unconditional
fall-through, and likewise prevented successful callbacks from
carrying their own Next or branch state.

Switch to direc use of do_lift_memop for GCSPOPM and GCSSS2 so their
selected Fault or Next result is propagated. For GCSSS1, retain the
result of both the successful register update and the fault assertion
rather than mapping them to unit.

GCSSS1 shares the CAS success/failure machinery with architectural
CAS, so parameterise the common helper over its lifting
function. Ordinary CAS continues through the existing lift_memop
adapter and therefore keeps its established unconditional-next
behaviour; GCSSS1 supplies a local adapter and can propagate its
control flow result.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
lift_shadow_stack duplicated an older subset of the AArch64 memory
lifting path solely so BL, BLR, and RET could return their own
control-flow result. Now that do_lift_memop accepts an explicit result
transformer, the duplicate is unnecessary and risks diverging from
future update in memory lifting path.

Switch to do_lift_memop for GCS branch shadow-stack accesses. This
retains their Jump or Fault result while reusing the common
translation and permission checking machinery.

Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
@fsestini

fsestini commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Thanks @murzinv , I'll take a look

@fsestini fsestini left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks @murzinv , I've checked the execution graphs generated from the test cases, and they look correct AFAICT. I have a couple of further comments, which I left in the code.

Comment thread herd/AArch64Sem.ml
in
let mv = read_reg_data rA ii in
do_cas quad Annot.N r ma mv mop_success mop_fail_with_wb mop_fail_no_wb false ii)
let lift_memop ?(tag = "") rA dir updatedb checked mop perms ma mv an ii =

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This function always receives a ~tag parameter, so making it optional is not needed.

Suggested change
let lift_memop ?(tag = "") rA dir updatedb checked mop perms ma mv an ii =
let lift_memop ~tag rA dir updatedb checked mop perms ma mv an ii =

Comment thread herd/AArch64Sem.ml
do_cas quad Annot.N r ma mv mop_success mop_fail_with_wb mop_fail_no_wb false ii)
let lift_memop ?(tag = "") rA dir updatedb checked mop perms ma mv an ii =
do_lift_memop ~tag rA dir updatedb checked mop perms ma mv an ii Fun.id DISide.Data in
do_cas_with (fun ~tag -> lift_memop ~tag) quad Annot.N r ma mv mop_success mop_fail_with_wb mop_fail_no_wb false ii)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Perhaps we could format this line so that it fits within 80-ish columns.

Comment thread herd/AArch64Sem.ml
(read_reg_ord r ii)
an
ii
Fun.id

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Codex pointed out that using do_lift_memop here may not be sufficient if PAC is enabled (-variant pauth1 or -variant pauth2). Indeed the pac path of do_lift_memop ignores the branch argument, and lift_pac_virt discards the memory operation's result.

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.

2 participants