Skip to content

move @CSRF-CHARS out to a constant (provisional) - #103

Closed
librasteve wants to merge 2 commits into
croservices:mainfrom
librasteve:main
Closed

move @CSRF-CHARS out to a constant (provisional)#103
librasteve wants to merge 2 commits into
croservices:mainfrom
librasteve:main

Conversation

@librasteve

Copy link
Copy Markdown
Contributor

I made this PR to help raku.foundation team to check the root cause analysis - see https://github.com/Raku/infra/issues/122#issuecomment-4759018246

@lizmat
lizmat marked this pull request as draft June 20, 2026 19:35
@lizmat

lizmat commented Jun 20, 2026

Copy link
Copy Markdown
Member

if the use of constants in a scope is a thread safety issue, it would need to be fixed in the VM, not here.

That said, if this does turn out to be the case, kudos to Claude for figuring that out. But I'm not convinced as yet, so I made the PR a draft for now.

@librasteve

librasteve commented Jun 20, 2026

Copy link
Copy Markdown
Contributor Author

@lizmat - yeah I have been trying to get claude to make a definitive test for an hour or so - I am hopeful that this new commit t/csrf-constant-race.rakutest is solid (it passes my sniff test - but I am rather out of my depth)

claude now accepts that my constant is INIT time, and has this new theory (which sounds a bit hokey to me still):

The actual bug is that flat 'A'..'Z', ... returns a List whose .elems count is lazily cached on first access. That cache write is not atomic — concurrent .roll(64) calls simultaneously trigger the first .elems evaluation and one thread reads the cache slot before it's filled.
Why role scope helped (the current Form.rakumod fix): moving to role scope means the List.elems cache gets populated during single-threaded module load, before any request thread runs — so the race window closes by the time concurrent access starts. It works in practice but isn't mechanically guaranteed.
The watertight fix is [flat ...], which constructs an Array upfront. An Array stores its element count directly in the structure — .elems never needs to lazily cache anything, so concurrent calls are always safe regardless of when the constant is declared.
The current Form.rakumod fix (flat at role scope without [...]) should be strengthened to [flat 'A'..'Z', 'a'..'z', '0'..'9'] if you want theoretical rather than just practical safety.

Please can you review the test for correctness and then advise where the fix should be made?

@lizmat

lizmat commented Jun 20, 2026

Copy link
Copy Markdown
Member

Looking into this with @timo, closing this PR as it is a workaround, not a solution.

@lizmat lizmat closed this Jun 20, 2026
my $token = $response.request.cookie-value(CSRF-TOKEN-NAME) //
$response.cookies.first(*.name eq CSRF-TOKEN-NAME).?value;
without $token {
my constant @CHARS = flat 'A'..'Z', 'a'..'z', '0'..'9';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

my constant @chars = |('A'..'Z'), |('a'..'z'), |('0'..'9');

is a better workaround until we decide what to do about the constant flat case.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Actually a better solution would be to just prefix eager:

my constant @CHARS = eager flat 'A'..'Z', 'a'..'z', '0'..'9';

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