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.
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.