traefik / yaegi

Yaegi is Another Elegant Go Interpreter
https://pkg.go.dev/github.com/traefik/yaegi
Apache License 2.0
6.94k stars 343 forks source link

Stuck in infinite for loop. #1518

Open kowalej-925 opened 1 year ago

kowalej-925 commented 1 year ago

The following code (part of golang.org/x/text/encoding/charmap) causes infinite loop.

// charmap.go Line 203
// Binary search in [low, high) for that rune in the m.charmap.encode table.
for low, high := int(m.charmap.low), 0x100; ; {
    if low >= high {
        err = internal.RepertoireError(m.charmap.replacement)
        break loop
    }
    mid := (low + high) / 2
    got := m.charmap.encode[mid]
    gotRune := rune(got & (1<<24 - 1))
    if gotRune < r {
        low = mid + 1
    } else if gotRune > r {
        high = mid
    } else {
        dst[nDst] = byte(got >> 24)
        nDst++
        break
    }
}

Expected result

Expected result is that the loop can iterate and break. If I run the code without Yaegi, via Go test framework, I can see that after the first iteration, the variable `low` gets incremented to the value of mid + 1. The next iteration of the loop shows that the new value of low is indeed mid + 1. This allows the binary search loop to continue, and eventually break out.

Got

Running in Yaegi (this is for a Traefik plugin), the value of `low` upon the second iteration, is the original value that was set by the variable initializer `for low, high := int(m.charmap.low)...`. Every time the loop iterates, this value stays the same. I have added print statements during debugging and confirmed that `low = mid + 1` is getting called, but again, `low` is just set to that initial value when I print it on second and any subsequent iterations.

If I write the loop like this, it works fine.

// Binary search in [low, high) for that rune in the m.charmap.encode table.
low, high := int(m.charmap.low), 0x100
for {
    if low >= high {
        err = internal.RepertoireError(m.charmap.replacement)
        break loop
    }
    mid := (low + high) / 2
    got := m.charmap.encode[mid]
    gotRune := rune(got & (1<<24 - 1))
    if gotRune < r {
        low = mid + 1
    } else if gotRune > r {
        high = mid
    } else {
        dst[nDst] = byte(got >> 24)
        nDst++
        break
    }
}


### Yaegi Version

v0.15.0

### Additional Notes

Yaegi is running in Traefik 2.9.8, in Docker container, using official image.

The loop code is verbatim from: https://github.com/golang/text/blob/30dadde3188b39150d373bac513a97df9d816a5b/encoding/charmap/charmap.go#L204
mvertes commented 1 year ago

I still have trouble to reproduce it from your sample, but I know that there are still some issues with golang.org/x/text/encoding/charmap. Keeping open as a bug for now.

kowalej-925 commented 1 year ago

Maybe it only occurs when using the actual module? Here's how I call it:

// Latin1Encode encodes string as Latin1.
func Latin1Encode(text string) (string, error) {
    latin1 := charmap.ISO8859_1.NewEncoder()
    textOut, err := latin1.String(text)
    if err != nil {
        return "", err
    }
    return textOut, err
}

Test string:

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhcmVuYV9hY2Nlc3MiOlt7ImFyZW5hX2lkIjoxNywicm9sZXMiOlsiUG9ydGFsIl19LHsiYXJlbmFfaWQiOjE2LCJyb2xlcyI6WyJQb3J0YWwiXX1dLCJzdWIiOjIyODEsImlhdCI6MTY3NzI3NDQwOSwiZXhwIjoxNjc3MzYwODA5fQ.ZOFezWrQlJuQ7ZaBDvA9hJtA2hkk207tSjC_WXpt0-gsXnvyBqR0Gp4qK-RQZBVxuGEnBMVdnq0kkslDTCm6Dxzf49gHKgm5MK7LbAJq3RrMFTxCLWdN1GC3Gnr1pDsS1llLtT0qWdeSDgGGqUX2RSFQvaWohuhyc5nwkMJTGPYInCsjPyr_i8-vOIWN3nN03A35HH6JbyvST1iRmRrFCb597G7mn17tC_OOKUk59JYCu5JP-HMaPdnhI9nx3_0-q40irJxJ0VyZWI4vjCMbr8P6vTD6pmb-ghaYK1LlB0OBM0ko-oCsGA4Voaevrj3oF5fx-OlZv_RWrMVzO3V_CvB0wqnnZGnYWLXEC-6fOyBF-z4mScWQMKPNR1kgAERbEFKhpphlryxZroKiQPNLpcbXqmucU2UWLAAbpnqCM-D3nftuwl7QJenaBRlz5EJL11FxUT4y3tgixKk_oWNpa2seiQb9YUeKOp6bUByPM3V03JOm2hYy7Z0f5ayGKvdQykq8sXePc_6ZFzkI3BO04yL0Ydy6oLsbUxQVVpLlk1rdJlVBZ29Xy6MozdG8xJ6ABlQcr2mdp_x7eUX9j3NvzfurHw81vknjj7B1DWTkGaTNB3qC89PyIWe0OWumFTLU59oSe1Q_j8JPNx9dLhQOUZx_YoOtiQZwP2bDYiwVaTI