{"record":{"id":"e36216ea74a2c9b3","repo":"fatedier/frp","slug":"invalid-effective-pool-count-d-cannot-safely-add","errorCode":null,"errorMessage":"invalid effective pool count %d, cannot safely add %d for work connection pool capacity","messagePattern":"invalid effective pool count (.+?), cannot safely add (.+?) for work connection pool capacity","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/control.go","lineNumber":449,"sourceCode":"\tctx           context.Context\n\tdoneCh        chan struct{}\n\tserverMetrics metrics.ServerMetrics\n}\n\nfunc NewControl(ctx context.Context, sessionCtx *SessionContext) (*Control, error) {\n\tif sessionCtx.LoginMsg.PoolCount < 0 {\n\t\treturn nil, fmt.Errorf(\"invalid pool count %d, must be non-negative\", sessionCtx.LoginMsg.PoolCount)\n\t}\n\tif sessionCtx.ServerCfg.Transport.MaxPoolCount < 0 {\n\t\treturn nil, fmt.Errorf(\n\t\t\t\"invalid max pool count %d, must be non-negative\",\n\t\t\tsessionCtx.ServerCfg.Transport.MaxPoolCount,\n\t\t)\n\t}\n\teffectivePoolCount := min(int64(sessionCtx.LoginMsg.PoolCount), sessionCtx.ServerCfg.Transport.MaxPoolCount)\n\tmaxPoolCountForChannel := int64(math.MaxInt) - int64(workConnPoolCapacityOffset)\n\tif effectivePoolCount > maxPoolCountForChannel {\n\t\treturn nil, fmt.Errorf(\n\t\t\t\"invalid effective pool count %d, cannot safely add %d for work connection pool capacity\",\n\t\t\teffectivePoolCount, workConnPoolCapacityOffset,\n\t\t)\n\t}\n\tpoolCount := int(effectivePoolCount)\n\tctl := &Control{\n\t\tsessionCtx:    sessionCtx,\n\t\tworkConnCh:    make(chan *proxy.WorkConn, poolCount+workConnPoolCapacityOffset),\n\t\tproxies:       make(map[string]proxy.Proxy),\n\t\tpoolCount:     poolCount,\n\t\tportsUsedNum:  0,\n\t\trunID:         sessionCtx.LoginMsg.RunID,\n\t\tstate:         controlStateCreated,\n\t\txl:            xlog.FromContextSafe(ctx),\n\t\tctx:           ctx,\n\t\tdoneCh:        make(chan struct{}),\n\t\tserverMetrics: metrics.Server,\n\t}","sourceCodeStart":431,"sourceCodeEnd":467,"githubUrl":"https://github.com/fatedier/frp/blob/6c8a8d0a97d03b44e9528d30b30c70cb9d61b405/server/control.go#L431-L467","documentation":"NewControl computes effectivePoolCount = min(client PoolCount, server MaxPoolCount) and checks it against math.MaxInt - workConnPoolCapacityOffset so that poolCount + offset cannot overflow int when sizing the workConnCh channel. An absurdly large value (near MaxInt) fails here instead of panicking on channel creation.","triggerScenarios":"A client (or misconfigured server max) supplies a pool count within a few units of 2^63-1 on 64-bit. make(chan ..., huge) would try to allocate an impossible buffer, so the guard rejects it first.","commonSituations":"Malicious or fuzzed login frames with extreme integers; config values accidentally set to huge numbers (e.g. pasting a bit pattern); custom clients reusing an uninitialised int that happens to be large.","solutions":["Cap pool counts to realistic values in config (single digits to low hundreds).","If you run a public frps, validate/limit login fields at the trust boundary before control creation.","Reject the client session — this value cannot be honoured, so retrying with the same number is futile."],"exampleFix":"# frps.toml — hard ceiling for any client\n[transport]\nmaxPoolCount = 100","handlingStrategy":"validation","validationCode":"const sanePoolMax = 1024\nif loginMsg.PoolCount > sanePoolMax || cfg.Transport.MaxPoolCount > sanePoolMax {\n    return fmt.Errorf(\"pool count unrealistically large\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Clamp pool counts at a trust boundary when running public frps.","Never feed unvalidated ints from the wire into resource sizing."],"tags":["go","frp","validation","overflow","hardening"],"backgroundTag":null,"analyzedSha":"6c8a8d0a97d03b44e9528d30b30c70cb9d61b405","analyzedAt":"2026-08-15T06:53:27.215Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}