Skip to content

fix(ollama): cap num_ctx so it cannot wrap negative when cast to int32#11032

Merged
mudler merged 2 commits into
mudler:masterfrom
Anai-Guo:fix-ollama-numctx-overflow
Jul 22, 2026
Merged

fix(ollama): cap num_ctx so it cannot wrap negative when cast to int32#11032
mudler merged 2 commits into
mudler:masterfrom
Anai-Guo:fix-ollama-numctx-overflow

Conversation

@Anai-Guo

Copy link
Copy Markdown
Contributor

What

applyOllamaOptions (core/http/endpoints/ollama/helpers.go) copied a client-supplied options.num_ctx straight into cfg.ContextSize with only a > 0 check:

if opts.NumCtx > 0 {
    cfg.ContextSize = &opts.NumCtx
}

That value is later cast to int32 before it reaches the backend (ContextSize: int32(ctxSize) in core/backend/options.go), so a num_ctx above math.MaxInt32 silently wraps into a negative context size that is then sent to the backend's LoadModel gRPC call. Both /api/chat and /api/generate route through applyOllamaOptions, so both Ollama-compatible endpoints shared the defect.

Fixes #11022.

Change

  • Cap num_ctx at math.MaxInt32 in applyOllamaOptions so the later int32 cast always stays positive.
  • Add helpers_internal_test.go (internal package ollama so it can exercise the unexported helper) with regression coverage for the overflow, in-range, and unset cases.

Scope note

num_ctx is an intentional user override in the Ollama API, so this change deliberately does not re-impose the hardware-aware autoContextSize clamp as an upper bound — applyOllamaOptions has 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

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 localai-org-maint-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Anai-Guo added a commit to Anai-Guo/LocalAI that referenced this pull request Jul 22, 2026
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>
@Anai-Guo
Anai-Guo force-pushed the fix-ollama-numctx-overflow branch from 6e7a857 to 41faba3 Compare July 22, 2026 01:09
@Anai-Guo

Copy link
Copy Markdown
Contributor Author

Thanks — you're right that the MaxInt32 cap only addressed the overflow symptom, not the DoS. Updated in 41faba3:

  • applyOllamaOptions now treats any existing positive cfg.ContextSize (populated by model config / hardware-aware defaults before this runs) as the server ceiling and clamps options.num_ctx down to it. A request can no longer raise the context window, so num_ctx=2,000,000,000 against a 4096/8192 model can't trigger a catastrophic KV-cache allocation. A smaller num_ctx is still honored.
  • The math.MaxInt32 bound is retained for the case where no smaller ceiling exists, keeping the later int32 cast safe.
  • Since /api/chat and /api/generate share applyOllamaOptions, both paths are covered by one fix. Added regression specs proving num_ctx=2,000,000,000 is clamped to an existing 4096 and 8192 ceiling (not replacing it), plus a spec that a smaller value still passes through.

@mudler
mudler merged commit bf19758 into mudler:master Jul 22, 2026
1 check passed
mudler added a commit that referenced this pull request Jul 22, 2026
…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>
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.

Bug 8: Unbounded num_ctx in /api/chat and /api/generate bypasses hardware-aware context-size clamping

3 participants