fix(ollama): cap num_ctx so it cannot wrap negative when cast to int32#11032
Conversation
applyOllamaOptions copied a client-supplied options.num_ctx straight into cfg.ContextSize with only a > 0 check. That value is later cast to int32 before it reaches the backend (core/backend/options.go), so a num_ctx above math.MaxInt32 silently wrapped into a negative context size that was then sent to the LoadModel gRPC call. Both /api/chat and /api/generate share applyOllamaOptions, so both endpoints were affected. Cap num_ctx at math.MaxInt32 so the later cast stays positive, and add internal regression coverage for the overflow, in-range, and unset cases. num_ctx remains an intentional user override, so this does not re-impose the hardware-aware auto context clamp; that policy choice is left to maintainers. Fixes mudler#11022 Signed-off-by: Tai An <antai12232931@outlook.com>
localai-org-maint-bot
left a comment
There was a problem hiding this comment.
The int32 overflow guard is useful, but this does not fix the primary bug reported in #11022. Capping to math.MaxInt32 still lets an unauthenticated request replace the hardware/model-derived context limit with ~2.1 billion tokens, so a real backend can still attempt a catastrophic KV-cache allocation. cfg.ContextSize has already been populated by model configuration / hardware-aware defaults before applyOllamaOptions runs; please treat that existing positive value as the server ceiling and clamp options.num_ctx to it (while retaining an int32-safe bound for configurations where no smaller ceiling exists). Add regression coverage where an oversized but in-range value such as 2,000,000,000 cannot replace an existing 4096/8192 ceiling, for both shared /api/chat and /api/generate option handling. The current test only proves the secondary overflow symptom and would pass while the DoS remains.
Per review on mudler#11032: capping only at math.MaxInt32 still let an unauthenticated request replace the hardware/model-derived context limit with ~2.1B tokens, so a real backend could attempt a catastrophic KV-cache allocation. Treat any existing positive cfg.ContextSize as the server ceiling and clamp num_ctx down to it (smaller values still honored), while retaining the int32-safe bound when no smaller ceiling exists. Shared by /api/chat and /api/generate via applyOllamaOptions. Add regression coverage proving num_ctx=2,000,000,000 cannot replace an existing 4096/8192 ceiling. Signed-off-by: Tai An <antai12232931@anaiguo.com>
Per review on mudler#11032: capping only at math.MaxInt32 still let an unauthenticated request replace the hardware/model-derived context limit with ~2.1B tokens, so a real backend could attempt a catastrophic KV-cache allocation. Treat any existing positive cfg.ContextSize as the server ceiling and clamp num_ctx down to it (smaller values still honored), while retaining the int32-safe bound when no smaller ceiling exists. Shared by /api/chat and /api/generate via applyOllamaOptions. Add regression coverage proving num_ctx=2,000,000,000 cannot replace an existing 4096/8192 ceiling. Signed-off-by: Tai An <antai12232931@outlook.com>
6e7a857 to
41faba3
Compare
|
Thanks — you're right that the MaxInt32 cap only addressed the overflow symptom, not the DoS. Updated in 41faba3:
|
…e builds (#11049) The num_ctx clamping specs added in #11032 construct their fixture with `config.ModelConfig{ContextSize: &existing}`, but ContextSize is not a direct field of ModelConfig: it belongs to LLMConfig, which ModelConfig embeds inline. Go allows reading a promoted field but not setting one in a composite literal, so the test file has never compiled: helpers_internal_test.go:33:31: unknown field ContextSize in struct literal of type "github.com/mudler/LocalAI/core/config".ModelConfig This broke `make lint` on master from bf19758 onward, and because the typecheck failure takes down the whole package it also reds tests-linux and tests-apple on every PR branched after that commit. Use the same literal form the rest of the tree already uses for this field (see core/backend/options_internal_test.go). Worth noting the specs were not merely uncompiled but inert: #11032 is a DoS fix (an unauthenticated client raising the context ceiling drives KV-cache allocation), and its regression guard was never actually running. Verified the restored specs are functional by stubbing out the ceiling clamp, which fails the "does not let an oversized num_ctx raise an existing context ceiling" spec as intended. Assisted-by: Claude Code:claude-opus-4-8[1m] [Read] [Edit] [Bash] Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
What
applyOllamaOptions(core/http/endpoints/ollama/helpers.go) copied a client-suppliedoptions.num_ctxstraight intocfg.ContextSizewith only a> 0check:That value is later cast to
int32before it reaches the backend (ContextSize: int32(ctxSize)incore/backend/options.go), so anum_ctxabovemath.MaxInt32silently wraps into a negative context size that is then sent to the backend'sLoadModelgRPC call. Both/api/chatand/api/generateroute throughapplyOllamaOptions, so both Ollama-compatible endpoints shared the defect.Fixes #11022.
Change
num_ctxatmath.MaxInt32inapplyOllamaOptionsso the laterint32cast always stays positive.helpers_internal_test.go(internalpackage ollamaso it can exercise the unexported helper) with regression coverage for the overflow, in-range, and unset cases.Scope note
num_ctxis an intentional user override in the Ollama API, so this change deliberately does not re-impose the hardware-awareautoContextSizeclamp as an upper bound —applyOllamaOptionshas no access to the loaded GGUF, and whether a user override should be allowed to exceed the auto-derived size is a policy decision. This PR only removes the silent negative-wrap. Happy to extend it if maintainers prefer a hardware-aware ceiling.🤖 Generated with Claude Code