Bug #121601 locking_service UDF dereferences a NULL integer argument and crashes mysqld
Submitted: 25 Oct 15:09
Reporter: Wwwwing Wwwwing Email Updates:
Status: Open Impact on me:
None 
Category:MySQL Server: User-defined functions ( UDF ) Severity:S2 (Serious)
Version:MySQL 8.4.11 official image OS:Any
Assigned to: CPU Architecture:Any

[25 Oct 15:09] Wwwwing Wwwwing
Description:
The `locking_service` UDFs validate only argument count and `UDF_ARGS::arg_type`
at init time. At row execution, `udf_handler::get_arguments()` sets each
`f_args.args[i]` to `nullptr` and fills the integer slot only when the argument is
not SQL NULL. A nullable `INT` column whose value is NULL therefore leaves
`args->args[arg_count - 1]` as a null pointer, and `service_get_read_locks()` /
`service_get_write_locks()` dereference it without a check.

The same defect was fixed for `keyring_udf` (the fix added a run-time NULL check);
the locking-service UDFs are an unpatched sibling of that pattern.

How to repeat:
```bash
IMG=mysql@sha256:85b9bf2e29cf836ecb8c2a15a935d4ba0c606631dff1dd79531a11983c638f2a
DATA=$(mktemp -d); chown 999:999 "$DATA"
docker run -d --name my52 --network none --read-only --cap-drop ALL \
  --security-opt no-new-privileges=true --pids-limit 512 --memory 2048m --cpus 2 \
  --user 999:999 --mount type=bind,src="$DATA",dst=/var/lib/mysql \
  --tmpfs /var/run/mysqld:rw,noexec,nosuid,nodev,size=16m,uid=999,gid=999 \
  -e MYSQL_ALLOW_EMPTY_PASSWORD=yes "$IMG" \
  --skip-name-resolve --log-error=/var/lib/mysql/error.log --log-error-verbosity=3

# wait until ready, then load the UDF functions (plugin library is in the image)
docker exec my52 mysql -uroot --socket=/var/run/mysqld/mysqld.sock -e "
  CREATE FUNCTION service_get_read_locks RETURNS INTEGER SONAME 'locking_service.so';
  CREATE FUNCTION service_get_write_locks RETURNS INTEGER SONAME 'locking_service.so';
  CREATE FUNCTION service_release_locks RETURNS INTEGER SONAME 'locking_service.so';
  CREATE DATABASE dsh;
  CREATE TABLE dsh.t(c INT NULL);
  INSERT INTO dsh.t VALUES (5),(NULL);"

# control: non-NULL value returns 1
docker exec my52 mysql -uroot --socket=/var/run/mysqld/mysqld.sock -e \
  "SELECT service_get_read_locks('dsh_ns','lock',5);"

# trigger: nullable column containing NULL
docker exec my52 mysql -uroot --socket=/var/run/mysqld/mysqld.sock -e \
  "SELECT service_get_read_locks('dsh_ns','lock',c) FROM dsh.t;"

sleep 3
docker inspect -f '{{.State.Running}} {{.State.ExitCode}}' my52
grep -a -E 'got signal|Query \(' "$DATA/error.log" | tail -5
```

## Actual result

The control returns `1`. The query over the nullable column crashes the server:

```text
ERROR 2013 (HY000): Lost connection to MySQL server during query
mysqld got signal 11 ;
Signal SIGSEGV (Address not mapped to object) at address 0x0
Query: SELECT service_get_read_locks('dsh_ns','lock',c) FROM dsh.t
```

The container exits non-zero and the server has to be restarted.

## Expected result

A NULL argument should produce an error (or a NULL result), not dereference a
null pointer. The UDF should validate `args->args[i] != nullptr` and
`args->null_value` at execution time.

## Impact

Any account with EXECUTE privilege on the UDF can crash mysqld after a DBA has
installed the `locking_service` plugin (the plugin is `MODULE_ONLY` and not
installed by default). This is a denial-of-service condition, not privilege
escalation, not data disclosure and not remote code execution.

## Source analysis

- `sql/locking_service_udf.cc:44-73` (`init_acquire`) checks `arg_count >= 3` and
  the last argument's `arg_type == INT_RESULT`, but not nullability.
- `sql/item_func.cc:4781` resets `f_args.args[i] = nullptr`; `:4789-4792` fills
  the INT_RESULT slot only when `!args[i]->null_value`.
- `sql/locking_service_udf.cc:87` and `:102` execute
  `*((long long *)args->args[args->arg_count - 1])` without a null check.
- `sql/CMakeLists.txt:1412-1415` builds `locking_service` as `MODULE_ONLY`.

Suggested fix:

Add a run-time null check before each dereference (mirroring the `keyring_udf`
`validate_run_time()` fix): return an error when `args->args[last] == nullptr`
or set `args->null_value`. Apply the same audit to other UDFs that take
non-constant integer arguments.