Bug #117906 User Defined Functions (UDF) have documented char *is_null, *error, however defined as unsigned char *
Submitted: 8 Apr 7:11 Modified: 15 Apr 4:23
Reporter: Daniel Black Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server Severity:S3 (Non-critical)
Version:8.0, 8.4 OS:Any
Assigned to: CPU Architecture:Any
Tags: UDFs

[8 Apr 7:11] Daniel Black
Description:
https://dev.mysql.com/doc/extending-mysql/8.0/en/adding-loadable-function.html#loadable-fu...

Has numerous reference to char *error and char *is_null.

Code however lists both as unsigned char *:
ref: https://github.com/mysql/mysql-server/blob/6b6d3ed3d5c6591b446276184642d7d0504ecc86/includ...

char in the C standard is undefined as to if its signed or unsigned so different architectures (looking at you PPC64 and ARM64) aren't always consistent (which isn't a problem for plugins that are always of same arch as the server).

If you compiled MySQL under cmake -DWITH_UBSAN=ON the error would be:

/home/dan/repos/mysql-server-8.4/sql/item_func.cc:4821:22: runtime error: call to function myfunc_double through pointer to incorrect function type 'double (*)(UDF_INIT *, UDF_ARGS *, unsigned char *, unsigned char *)'

How to repeat:
Adjust MySQL example code conform to documented ABI:

diff --git a/sql/udf_example.cc b/sql/udf_example.cc
index 79027c40873..e8f03a3c470 100644
--- a/sql/udf_example.cc
+++ b/sql/udf_example.cc
@@ -248,8 +248,8 @@ static char codes[26] = {
 #define NOGHTOF(x) (codes[(x) - 'A'] & 16) /* BDH */
 
 extern "C" char *metaphon(UDF_INIT *, UDF_ARGS *args, char *result,
-                          unsigned long *length, unsigned char *is_null,
-                          unsigned char *) {
+                          unsigned long *length, char *is_null,
+                          char *) {
   const char *word = args->args[0];
   const char *w_end;
   char *org_result;
@@ -471,7 +471,7 @@ extern "C" bool myfunc_double_init(UDF_INIT *initid, UDF_ARGS *args,
 }
 
 extern "C" double myfunc_double(UDF_INIT *, UDF_ARGS *args,
-                                unsigned char *is_null, unsigned char *) {
+                                char *is_null, char *) {
   unsigned long val = 0;
   unsigned long v = 0;
   unsigned i, j;

$ clang --version
clang version 19.1.7 (Fedora 19.1.7-3.fc41)

$ cmake -DWITH_UBSAN=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ../mysql-server-8.4 
$ cmake --build .
$ UBSAN_OPTIONS=print_stacktrace=1,halt_on_error=1 mysql-test/mtr  udf
Logging: /home/dan/repos/mysql-server-8.4/mysql-test/mysql-test-run.pl  udf
MySQL Version 8.4.4
Checking supported features
Using 'all' suites
Collecting tests
Checking leftover processes
Removing old var directory
Creating var directory '/home/dan/repos/build-mysql-server-8.4/mysql-test/var'
Installing system database
Using parallel: 1

==============================================================================
                  TEST NAME                       RESULT  TIME (ms) COMMENT
------------------------------------------------------------------------------
[ 50%] main.udf                                  [ fail ]
        Test ended at 2025-04-08 16:57:15

CURRENT_TEST: main.udf
mysqltest: At line 53: Query 'select myfunc_double(1)' failed.
ERROR 2013 (HY000): Lost connection to MySQL server during query

The result from queries just before the failure was:
...
select myfunc_double();
ERROR HY000: Can't initialize function 'myfunc_double'; myfunc_double must have at least one argument
select myfunc_double(1);
safe_process[346005]: Child process: 346006, exit: 1

...
2025-04-08T06:57:15.368777Z 0 [Note] [MY-011243] [Server] Plugin mysqlx reported: 'Using OpenSSL for TLS connections'
2025-04-08T06:57:15.368925Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 13020, socket: /home/dan/repos/build-mysql-server-8.4/mysql-test/var/tmp/mysqlx.1.sock
2025-04-08T06:57:15.368947Z 0 [System] [MY-010931] [Server] /home/dan/repos/build-mysql-server-8.4/runtime_output_directory/mysqld: ready for connections. Version: '8.4.4-ubsan'  socket: '/home/dan/repos/build-mysql-server-8.4/mysql-test/var/tmp/mysqld.1.sock'  port: 13000  Source distribution.
/home/dan/repos/mysql-server-8.4/sql/item_func.cc:4821:22: runtime error: call to function myfunc_double through pointer to incorrect function type 'double (*)(UDF_INIT *, UDF_ARGS *, unsigned char *, unsigned char *)'
/home/dan/repos/mysql-server-8.4/sql/udf_example.cc:474: note: myfunc_double defined here
    #0 0x0000012bbefc in udf_handler::val_real(bool*) /home/dan/repos/mysql-server-8.4/sql/item_func.cc:4821:22
    #1 0x0000012bd05b in Item_func_udf_float::val_real() /home/dan/repos/mysql-server-8.4/sql/item_func.cc:5061:14
    #2 0x00000112f3af in Item::send(Protocol*, String*) /home/dan/repos/mysql-server-8.4/sql/item.cc:7597:25
    #3 0x000000647dee in THD::send_result_set_row(mem_root_deque<Item*> const&) /home/dan/repos/mysql-server-8.4/sql/sql_class.cc:2897:15
    #4 0x0000017217e0 in Query_result_send::send_data(THD*, mem_root_deque<Item*> const&) /home/dan/repos/mysql-server-8.4/sql/query_result.cc:103:12
    #5 0x000000af68b7 in Query_expression::ExecuteIteratorQuery(THD*) /home/dan/repos/mysql-server-8.4/sql/sql_union.cc:1793:25
    #6 0x000000af7867 in Query_expression::execute(THD*) /home/dan/repos/mysql-server-8.4/sql/sql_union.cc:1834:10
    #7 0x00000096753d in Sql_cmd_dml::execute_inner(THD*) /home/dan/repos/mysql-server-8.4/sql/sql_select.cc:1059:15
    #8 0x000000962fa1 in Sql_cmd_dml::execute(THD*) /home/dan/repos/mysql-server-8.4/sql/sql_select.cc:782:7
    #9 0x000000807832 in mysql_execute_command(THD*, bool) /home/dan/repos/mysql-server-8.4/sql/sql_parse.cc:4737:29
    #10 0x0000007fdc29 in dispatch_sql_command(THD*, Parser_state*) /home/dan/repos/mysql-server-8.4/sql/sql_parse.cc:5392:19
    #11 0x0000007f0286 in dispatch_command(THD*, COM_DATA const*, enum_server_command) /home/dan/repos/mysql-server-8.4/sql/sql_parse.cc:2136:7
    #12 0x0000007f9153 in do_command(THD*) /home/dan/repos/mysql-server-8.4/sql/sql_parse.cc:1465:18
    #13 0x000000cd3363 in handle_connection(void*) /home/dan/repos/mysql-server-8.4/sql/conn_handler/connection_handler_per_thread.cc:304:13
    #14 0x000003d61e95 in pfs_spawn_thread(void*) /home/dan/repos/mysql-server-8.4/storage/perfschema/pfs.cc:3061:3
    #15 0x7f3f70b97ba7 in start_thread (/lib64/libc.so.6+0x70ba7) (BuildId: f83d43b9b4b0ed5c2bd0a1613bf33e08ee054c93)
    #16 0x7f3f70c1bb8b in __GI___clone3 (/lib64/libc.so.6+0xf4b8b) (BuildId: f83d43b9b4b0ed5c2bd0a1613bf33e08ee054c93)

SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /home/dan/repos/mysql-server-8.4/sql/item_func.cc:4821:22 
safe_process[345951]: Child process: 345952, exit: 1
----------SERVER LOG END-------------

 - the logfile can be found in '/home/dan/repos/build-mysql-server-8.4/mysql-test/var/log/main.udf/udf.log'

[100%] shutdown_report                           [ pass ]       
------------------------------------------------------------------------------
The servers were restarted 0 times
The servers were reinitialized 0 times
Spent 0.000 of 9 seconds executing testcases

Completed: Failed 1/2 tests, 50.00% were successful.

Failing test(s): main.udf

The log files in var/log may give you some hint of what went wrong.

If you want to report this error, please read first the documentation
at http://dev.mysql.com/doc/mysql/en/mysql-test-suite.html

mysql-test-run: *** ERROR: there were failing test cases

Suggested fix:
Update documentation to refer to "unsigned char *" as the type for is_null and error arguments.

The proliferation of the unsigned char * as a stable ABI has propagated beyond MySQL:

https://github.com/pluots/sql-udf/blob/35a9bf5a122ee138ae29a0b5aae013f5035dee22/udf-macros...
https://github.com/pluots/sql-udf/blob/35a9bf5a122ee138ae29a0b5aae013f5035dee22/udf/src/wr...

For those that haven't noticed, probably because the error /is_null really only get pushed boolean matters it won't change.

For those engaged with type safe wrappers and checking who would have moved to unsigned char, and change of ABI would be noticed. So safest just to update documentation.
[8 Apr 8:19] MySQL Verification Team
Hello Daniel Black,

Thank you for the report and feedback.

regards,
Umesh
[14 Apr 21:48] Jon Stephens
So... Have the changes suggested by the submitter for udf_example.cc been implemented? Will we implement them?

If we need to update the example as shown in the Manual, this--and how it needs to be updated--needs to be stated clearly.

Please don't just dump stuff on us and expect us to unravel it ourselves.

Thanks!!
[15 Apr 3:11] Daniel Black
Per Suggested Fix, only a documentation update is expected on "adding-loadable-function.html" (I haven't found another page yet that needs updated).

The code change was just to demonstrate that the documented interface resulted in undefined behaviour compared to the server implementation ABI and no code change is expected from this bug report.