Skip to content

TinyJson: Stack overflow from unbounded recursion in ParseValue/ParseObject #233

@prateek-sirah

Description

@prateek-sirah

TinyJson: Stack overflow from unbounded recursion in ParseValue/ParseObject

Summary

Nakama.TinyJson.JsonParser.ParseValue and ParseObject call each other recursively with no depth limit. On platforms with limited stack size (Android ARM64 with IL2CPP = 1MB default), moderately nested JSON or types with nested object fields cause a StackOverflowException that crashes the application.

This is the same vulnerability class as CVE-2024-7254 (Google Protobuf unbounded recursion DoS).

Impact

  • ~80 crashes/day across two production Android games (build 1.4.210)
  • Affects both games because Nakama.dll is shared
  • Crash in [libil2cpp.so] — stack overflow manifests as a native crash, not a managed exception
  • Same Crashlytics issue ID appears across multiple games using the same Nakama client version

Reproduction

Any JSON payload parsed by TinyJson with sufficient nesting depth will trigger this. The exact threshold depends on platform stack size:

  • Android ARM64 IL2CPP: ~1MB stack → crashes at relatively shallow depth
  • Desktop/Editor: larger stack → harder to reproduce without deeply nested payloads

Root Cause (IL Analysis)

Decompiled Nakama.dll v3.21.1 with System.Reflection.Metadata:

  • ParseValue (1160 bytes IL): calls ParseObject when JSON token is {
  • ParseObject (267 bytes IL): calls ParseValue for each field value
  • Neither method has a recursion depth check
  • ParseObject is small (~32 bytes ARM64 = ~8 instructions) — the mutual recursion burns through the stack quickly

Suggested Fix

Add a [ThreadStatic] depth counter to ParseValue:

[ThreadStatic] static int _parseDepth;
const int MaxParseDepth = 64;

object ParseValue(Type type, string json)
{
    if (++_parseDepth > MaxParseDepth)
    {
        _parseDepth--;
        return null; // or throw a descriptive exception
    }
    try
    {
        // existing ParseValue logic
    }
    finally
    {
        _parseDepth--;
    }
}

This matches the approach used to fix CVE-2024-7254 in protobuf-java (recursion depth limit).

Workaround

We are currently maintaining a patched build of Nakama.dll from the v3.21.1 tag with the above depth guard applied. This is fragile — the patch must be re-applied on every SDK update.

Environment

  • Nakama .NET Client: 3.21.1 (via nakama-unity)
  • Unity: 6000.4.0f1
  • Platform: Android ARM64 (IL2CPP)
  • Crashlytics issue: 8f0be5b4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions