Skip to content

Socket byte encoding overflows for seed-sphere Level 5 on any element other than Fire, crashing the client #891

Description

@didiconcs

Summary

ItemSerializerHelper.SetSocketBytes encodes each socket's seed-sphere level + element + option number into a single byte:

var optionIndex = SocketOptionIndexOffsets[elementType] + elementOption;
return (byte)((sphereLevel * MaximumSocketOptions) + optionIndex);

(src/GameServer/RemoteView/ItemSerializerHelper.cs:141-146, MaximumSocketOptions = 50, SocketOptionIndexOffsets = { 0, 10, 16, 21, 29, 36 } for Fire/Water/Ice/Wind/Lightning/Earth respectively, line 62)

For sphereLevel = 5 (the maximum socket bonus tier) on any element other than Fire, sphereLevel * 50 + optionIndex starts at 250 + 10 = 260 for Water and only grows for the later elements, this exceeds byte.MaxValue (255) and silently wraps around via the implicit (byte) cast, producing a completely unrelated index. Fire is the only element that stays in range at level 5 (250 + optionIndex(0..5) ≤ 255).

Example: level 5, Water, option number 4 → optionIndex = 10 + 4 = 14250 + 14 = 264 → cast to byte8 (wrapped), instead of a value the client can make sense of.

Impact, reachable through normal gameplay, not just admin tools

This isn't limited to GM/SQL-crafted items. Tracing the standard crafting chain:

  • "Seed Creation" (Seed Master NPC) randomly rolls a Seed's Level up to ItemDefinition.MaximumItemLevel, which is 5 for every element alike (Fire, Water, Ice, Wind, Lightning, Earth), CreateSeed() computes it from the same per-element ItemOptionDefinition that's also used for the corresponding Seed Sphere (src/Persistence/Initialization/VersionSeasonSix/Items/SocketSystem.cs).
  • "Seed Sphere Creation" copies that level onto the resulting Seed Sphere (SeedSphereCrafting.cs: result.Level = seed.Level).
  • "Mount Seed Sphere" copies it again onto the equipped item's socket (MountSeedSphereCrafting.cs: sphereOption.Level = seedSphere.Level).

So a player who legitimately rolls a Level-5 Water/Ice/Wind/Lightning/Earth seed through normal RNG and mounts it ends up with an item whose socket byte silently wraps around on every serialization. When that item is sent to a client (equipped, viewed in a shop/trade window, shown to another player, etc.), the corrupted byte is used by the client to index into a fixed-size table. We reproduced this with a real client (Season 6, English protocol build): it writes out of bounds and crashes immediately with a stack-corruption error (Run-Time Check Failure #2 - Stack around the variable 'iSeedSum' was corrupted) as soon as the item is loaded/rendered.

Suggested fix

The offset table was reverse-engineered from the original client/protocol (per the comment on line 59, "found these index offsets by trial and error"), so the safe range is a client-side constraint OpenMU should defend against server-side rather than assume is never reached:

  • Cap ItemDefinition.MaximumItemLevel for non-Fire Seeds/Seed Spheres at 4 instead of 5 (the safe range holds for every element through level 4: 4*50 + maxOptionIndexForThatElement ≤ 255), or
  • Rework the encoding so level 5 fits in a byte for every element, if that's what the real client protocol expects.

Either way, an explicit bounds check (or at least a guarding comment) would help, today even the AdminPanel's item option editor lets you freely set Level=5 on a non-Fire socket option with no warning, so the unsafe state is easy to reintroduce even after a data fix.

Environment

  • Confirmed against origin/master (commit b29338b78).
  • Reproduced with a real MuMain (Season 6, English protocol) client, crash occurs immediately on entering the map with the affected item equipped.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions