pingcap / pingcap/tidb

max_connect_errors=1 is accepted but TiDB does not block a host after an aborted handshake

Open
#70,992 0 comments 0 reactions 0 assignees View on GitHub
affects-9.0 component/server found-by-ai may-affects-25.10 may-affects-26.3 may-affects-7.5 may-affects-8.1 may-affects-8.5 severity/major sig/sql-infra type/bug
Dominant language
Go
Stars
40.5k
Forks
6.2k
PR merge metrics
PR metrics pending

Description

## Bug Report

Please answer these questions before submitting your issue. Thanks!

### 1. Minimal reproduce step (Required)

```sql
-- On both engines, create a probe user reachable from a non-loopback client IP.
CREATE USER 'maxconn_probe'@'%' IDENTIFIED BY 'probe';

SET GLOBAL max_connect_errors=1;
SELECT @@global.max_connect_errors;
-- MySQL: also TRUNCATE TABLE performance_schema.host_cache;
```

From the probe IP, open a raw TCP socket, read the initial handshake, and close without sending a response. Then attempt a valid connection:

```bash
mysql -h -P -umaxconn_probe -pprobe -e "SELECT 'PROBE_LOGIN_OK';"
```

Probe output:

```text
MYSQL_INITIAL_HANDSHAKE_BYTES=77
MYSQL_AFTER_ABORT_RC=1
MYSQL_AFTER_ABORT_ERR=ERROR 1129 (HY000): Host '192.168.206.32' is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'
MYSQL_MAX_CONNECT_ERRORS_ENFORCED

TIDB_INITIAL_HANDSHAKE_BYTES=112
TIDB_AFTER_ABORT_RC=0
TIDB_AFTER_ABORT_OUT='PROBE_LOGIN_OK'
TIDB_MAX_CONNECT_ERRORS_IGNORED
```

Both engines reported `@@global.max_connect_errors=1` before the abort. MySQL blocked the probe IP after one aborted handshake; TiDB allowed the valid connection from the same IP.

### 2. What did you expect to see? (Required)

After `max_connect_errors` successive handshake failures from one host, the server must block that host with MySQL-compatible `ERROR 1129` until the host cache is flushed. If TiDB does not support host-error blocking, `SET GLOBAL max_connect_errors` must fail explicitly instead of reporting a value that the connection path ignores.

### 3. What did you see instead (Required)

MySQL 8.3: max_connect_errors=1; truncate host cache; one raw aborted handshake from 192.168.206.32; valid probe-user connection -> ERROR 1129. TiDB a514: max_connect_errors=1; one raw aborted handshake from 192.168.206.32; valid probe-user connection -> PROBE_LOGIN_OK.

### 4. What is your TiDB version? (Required)

```text
Release Version: v8.4.0-this-is-a-placeholder
Edition: Community
Git Commit Hash: None
Git Branch: None
UTC Build Time: None
GoVersion: go1.25.12
Race Enabled: false
Check Table Before Drop: false
Store: unistore
Kernel Type: Classic
```

Built from source commit `a514a92784c9654502686e6ee6efc9e0aeda8afa` (pingcap/tidb master, 2026-09-07).

### 5. Root cause (optional)

- pkg/sessionctx/variable/noop.go:74 registers max_connect_errors as a noop variable
- pkg/sessionctx/variable/noop.go:141 registers host_cache_size as a noop variable
- pkg/errno/errname.go:155 defines ErrHostIsBlocked but no pkg/server connection path uses it
- no pkg/server path counts per-host handshake errors or reads max_connect_errors/host_cache_size

Contributor guide

Open the contributing guide

Research direction

Start with the noop registrations in pkg/sessionctx/variable/noop.go and ErrHostIsBlocked in pkg/errno/errname.go, then trace the pkg/server connection and handshake path. Reproduce the aborted-handshake sequence from the issue and inspect how host errors, max_connect_errors, and host_cache_size are currently handled. Done means repeated handshake failures produce MySQL-compatible ERROR 1129, or unsupported settings fail explicitly, with regression coverage.

Written by the indexing model from the issue text.

Assessment

Tech stack
go, mysql
Domain
backend, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.