Skip to content
TORNLIFE More

Slots Racing Condition

Started by Gibbby [2619420] on in Bugs & Issues.

5 replies · 43 views · thread synced · 10 days ago · View on torn.com
About this thread

Posts archived: 6 / 6 posts (100%) · the total is Torn's reply count + the opening post at the last fetch

Counted by TornLife from the archived posts.

Archived posts
6
Discussion span
→
People posting
5
Likes on archived posts
2
Posts by staff, officers and moderators
2
Authority score
56 / 100
Historical score
28 / 100
Story score
36 / 100
Engagement score
47 / 100
Gibbby [2619420]

Found a race condition in the casino slots spin endpoint that bypasses the "one spin at a time" lock.

 

The slots spin lock uses a non-atomic check-then-set pattern. By sending multiple concurrent requests to the spin endpoint, 2-5 spins execute before the lock engages. The server returns "A slots spin is already in action" for the rest, but the ones that slip through are fully processed and charged.

 

How to Reproduce

1. Go to the Slots page

2. Open browser DevTools console (F12)

3. Paste and run:

 

const rfcv = document.match(/rfc_v=([a-f0-9]+)/)[1];

const promises = [];

for (let i = 0; i < 20; i++) {

  promises.push(fetch('/page.php?sid=slotsData&step=play&stake=10000&rfcv=' + rfcv + '&_r=' + i, {

    credentials: 'same-origin',

    headers: { 'X-Requested-With': 'XMLHttpRequest', 'Referer': 'https://www.torn.com/page.php?sid=slots' }

  }).then(r => r.json()));

}

const results = await Promise.all(promises);

console.log('Spins:', results.filter(r => r.won !== undefined).length, '/ Locked:', results.filter(r => r.error?.includes('already')).length);

 

Expected: 1 spin executes, 19 locked

Actual: 2-5 spins execute, 15-18 locked

IndyCision [2597200]

Daft question, but are you able to click it fast enough to reproduce this bug normally without using an automated script to spam it?

 

(Even if you had to add your own button that sends the request rather than clicking the spin button, which would have been OK?)

aurel1 [1046304] Admin Staff

Have you actually checked, how much time all 20 requests took? I tried exactly the same way and received 8 spins and 12 locks. However according to locks 7 second passed since the first completed request - so it resulted in 8 legal spins (7 + 1, as the first one will always pass).

 

"The slots spin lock uses a non-atomic check-then-set pattern" - Torn is using session based locking, you can't create a racing condition using only a single active session, as all requests are queued.

Gibbby [2619420]

All 20 requests sent within 4ms of each other (simultaneously). Results:

Spin 1: sent 0ms  → completed  156ms
Spin 2: sent 1ms  → completed 1055ms
Spin 3: sent 2ms  → completed 1927ms
Spin 4: sent 2ms  → completed 3078ms
Spin 5: sent 4ms  → completed 4009ms
Spin 6: sent 2ms  → completed 5033ms
Spin 7: sent 1ms  → completed 6061ms

 

7 spins, ~1 second apart, 6 seconds total. 13 received "already in action."

You're right that session locking serializes the requests — they process sequentially, not simultaneously. Each spin takes ~150ms server-side, then the session lock releases and the next queued request starts.

The question is whether this is intended. The "A slots spin is already in action" error implies the lock should persist for the entire spin cycle, not just the server processing time. From the UI, a normal player waits for the spin animation (5-8 seconds) before spinning again. But queued fetch requests bypass the animation wait, getting 7 spins in 6 seconds from a single action — roughly 5-7x the intended rate.

CloudJumper [1636201]Staff

Based on Joe and Pavel's messages, everything is as expected here, there's nothing that needs to be done from the dev side. I will close the report.