Skip to content

fix(memstore): UintDefault rejected every positive int - #2608

Open
Qalipso wants to merge 1 commit into
kataras:mainfrom
Qalipso:fix_uint_default_int_overflow
Open

fix(memstore): UintDefault rejected every positive int#2608
Qalipso wants to merge 1 commit into
kataras:mainfrom
Qalipso:fix_uint_default_int_overflow

Conversation

@Qalipso

@Qalipso Qalipso commented Jul 28, 2026

Copy link
Copy Markdown

UintDefault returns the default value and a not-found error for any plain int, on every 64-bit platform.

var p memstore.Store
p.Set("n", 8)
p.GetUintDefault("n", 0) // returns 0, not 8

Cause

var maxValue uint64 = math.MaxUint32
if x64 {
    maxValue = math.MaxUint64
}
...
case int:
    if vv < 0 || vv > int(maxValue) {
        return def, e.notFound(uintType)
    }

On a 64-bit platform maxValue is math.MaxUint64, so the conversion int(maxValue) wraps to -1. The guard becomes vv > -1, which is true for every positive int, so the value is rejected.

Fix

int and uint have the same width in Go, so math.MaxInt <= math.MaxUint always holds and every non-negative int is representable as a uint. Only the sign needs checking. The comparison against maxValue is removed from the int case; maxValue is still used by the uint64 case, which is correct as written (on 32-bit it properly rejects values above math.MaxUint32).

The sized getters (Uint8Default and friends) use literal bounds such as math.MaxUint8 and were never affected.

Verification

The three added tests fail on main and pass with the fix:

before:  --- FAIL: TestUintDefaultInt      expected 8 but got 0
         --- FAIL: TestUintDefaultMaxInt   not found: n as uint (uint)
after:   ok  github.com/kataras/iris/v12/core/memstore

TestUintDefaultNegativeInt passes both before and after, confirming the sign check is preserved. go vet, go test -race and gofmt are clean, ./core/... is green, and the package still builds and vets for GOARCH=386 and GOARCH=arm where uint is 32-bit.


Two notes:

  1. This is independent of test(memstore): add tests for Store operations and Entry conversions #2607 (tests only, adds new files); the two do not touch the same lines and can merge in either order.

  2. While confirming the sign behaviour I noticed the sized signed cases do not check the sign at all, so a negative value wraps silently rather than being rejected like the int case:

    int8(-1)  -> 18446744073709551615, err=nil
    int16(-1) -> 18446744073709551615, err=nil
    int32(-1) -> 18446744073709551615, err=nil
    int64(-1) -> 18446744073709551615, err=nil
    int(-1)   -> default, err=not found     <- correct
    

    I left that alone to keep this PR to one change. Happy to send it separately if you agree it should behave like the int case.

I did not open an issue first as the guidelines suggest — the diff is one line and the reproducer is above, but I'm glad to open one if you'd prefer.

UintDefault compared an int value against maxValue, which is
math.MaxUint64 on a 64-bit platform. Converting it back with
int(maxValue) wraps to -1, so `vv > int(maxValue)` was true for every
positive int and the method returned the default and a not-found error
for any plain int value:

    var p memstore.Store
    p.Set("n", 8)
    p.GetUintDefault("n", 0) // returned 0, not 8

int and uint have the same width, so every non-negative int is
representable as a uint and only the sign needs checking. The sized
unsigned getters use literal bounds and were not affected.
@Qalipso
Qalipso requested a review from kataras as a code owner July 28, 2026 16:35
@CLAassistant

CLAassistant commented Jul 28, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

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