1 /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation,
21 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
22
23 /* This implements 'user defined functions' */
24
25 /*
26 Known bugs:
27
28 Memory for functions is never freed!
29 Shared libraries are not closed before mysqld exits;
30 - This is because we can't be sure if some threads are using
31 a function.
32
33 The bugs only affect applications that create and free a lot of
34 dynamic functions, so this shouldn't be a real problem.
35 */
36
37 #include "sql_base.h" // close_mysql_tables
38 #include "sql_parse.h" // check_string_char_length
39 #include "sql_table.h" // write_bin_log
40 #include "records.h" // init_read_record, end_read_record
41 #include "lock.h" // MYSQL_LOCK_IGNORE_TIMEOUT
42 #include "log.h"
43 #include "sql_plugin.h" // check_valid_path
44
45 #ifdef HAVE_DLFCN_H
46 #include <dlfcn.h>
47 #endif
48
49 #ifdef HAVE_DLOPEN
50 #include <stdarg.h>
51 #include <hash.h>
52
53 static bool initialized = 0;
54 static MEM_ROOT mem;
55 static HASH udf_hash;
56 static mysql_rwlock_t THR_LOCK_udf;
57
58
59 static udf_func *add_udf(LEX_STRING *name, Item_result ret,
60 char *dl, Item_udftype typ);
61 static void del_udf(udf_func *udf);
62 static void *find_udf_dl(const char *dl);
63
init_syms(udf_func * tmp,char * nm)64 static char *init_syms(udf_func *tmp, char *nm)
65 {
66 char *end;
67
68 if (!((tmp->func= (Udf_func_any) dlsym(tmp->dlhandle, tmp->name.str))))
69 return tmp->name.str;
70
71 end=my_stpcpy(nm,tmp->name.str);
72
73 if (tmp->type == UDFTYPE_AGGREGATE)
74 {
75 (void)my_stpcpy(end, "_clear");
76 if (!((tmp->func_clear= (Udf_func_clear) dlsym(tmp->dlhandle, nm))))
77 return nm;
78 (void)my_stpcpy(end, "_add");
79 if (!((tmp->func_add= (Udf_func_add) dlsym(tmp->dlhandle, nm))))
80 return nm;
81 }
82
83 (void) my_stpcpy(end,"_deinit");
84 tmp->func_deinit= (Udf_func_deinit) dlsym(tmp->dlhandle, nm);
85
86 (void) my_stpcpy(end,"_init");
87 tmp->func_init= (Udf_func_init) dlsym(tmp->dlhandle, nm);
88
89 /*
90 to prefent loading "udf" from, e.g. libc.so
91 let's ensure that at least one auxiliary symbol is defined
92 */
93 if (!tmp->func_init && !tmp->func_deinit && tmp->type != UDFTYPE_AGGREGATE)
94 {
95 if (!opt_allow_suspicious_udfs)
96 return nm;
97 sql_print_warning(ER(ER_CANT_FIND_DL_ENTRY), nm);
98 }
99 return 0;
100 }
101
102
get_hash_key(const uchar * buff,size_t * length,my_bool not_used MY_ATTRIBUTE ((unused)))103 extern "C" uchar* get_hash_key(const uchar *buff, size_t *length,
104 my_bool not_used MY_ATTRIBUTE((unused)))
105 {
106 udf_func *udf=(udf_func*) buff;
107 *length=(uint) udf->name.length;
108 return (uchar*) udf->name.str;
109 }
110
111 static PSI_memory_key key_memory_udf_mem;
112
113 #ifdef HAVE_PSI_INTERFACE
114 static PSI_rwlock_key key_rwlock_THR_LOCK_udf;
115
116 static PSI_rwlock_info all_udf_rwlocks[]=
117 {
118 { &key_rwlock_THR_LOCK_udf, "THR_LOCK_udf", PSI_FLAG_GLOBAL}
119 };
120
121 static PSI_memory_info all_udf_memory[]=
122 {
123 { &key_memory_udf_mem, "udf_mem", PSI_FLAG_GLOBAL}
124 };
125
init_udf_psi_keys(void)126 static void init_udf_psi_keys(void)
127 {
128 const char* category= "sql";
129 int count;
130
131 count= array_elements(all_udf_rwlocks);
132 mysql_rwlock_register(category, all_udf_rwlocks, count);
133
134 count= array_elements(all_udf_memory);
135 mysql_memory_register(category, all_udf_memory, count);
136 }
137 #endif
138
139 /*
140 Read all predeclared functions from mysql.func and accept all that
141 can be used.
142 */
143
udf_init()144 void udf_init()
145 {
146 udf_func *tmp;
147 TABLE_LIST tables;
148 READ_RECORD read_record_info;
149 TABLE *table;
150 int error;
151 DBUG_ENTER("ufd_init");
152 char db[]= "mysql"; /* A subject to casednstr, can't be constant */
153
154 if (initialized)
155 DBUG_VOID_RETURN;
156
157 #ifdef HAVE_PSI_INTERFACE
158 init_udf_psi_keys();
159 #endif
160
161 mysql_rwlock_init(key_rwlock_THR_LOCK_udf, &THR_LOCK_udf);
162
163 init_sql_alloc(key_memory_udf_mem, &mem, UDF_ALLOC_BLOCK_SIZE, 0);
164 THD *new_thd = new THD;
165 if (!new_thd ||
166 my_hash_init(&udf_hash,system_charset_info,32,0,0,get_hash_key, NULL, 0,
167 key_memory_udf_mem))
168 {
169 sql_print_error("Can't allocate memory for udf structures");
170 my_hash_free(&udf_hash);
171 free_root(&mem,MYF(0));
172 delete new_thd;
173 DBUG_VOID_RETURN;
174 }
175 initialized = 1;
176 new_thd->thread_stack= (char*) &new_thd;
177 new_thd->store_globals();
178 {
179 LEX_CSTRING db_lex_cstr= { STRING_WITH_LEN(db) };
180 new_thd->set_db(db_lex_cstr);
181 }
182
183 tables.init_one_table(db, sizeof(db)-1, "func", 4, "func", TL_READ);
184
185 if (open_and_lock_tables(new_thd, &tables, MYSQL_LOCK_IGNORE_TIMEOUT))
186 {
187 DBUG_PRINT("error",("Can't open udf table"));
188 sql_print_error("Can't open the mysql.func table. Please "
189 "run mysql_upgrade to create it.");
190 goto end;
191 }
192
193 table= tables.table;
194 if (init_read_record(&read_record_info, new_thd, table, NULL, 1, 1, FALSE))
195 goto end;
196 table->use_all_columns();
197 while (!(error= read_record_info.read_record(&read_record_info)))
198 {
199 DBUG_PRINT("info",("init udf record"));
200 LEX_STRING name;
201 name.str=get_field(&mem, table->field[0]);
202 name.length = strlen(name.str);
203 char *dl_name= get_field(&mem, table->field[2]);
204 bool new_dl=0;
205 Item_udftype udftype=UDFTYPE_FUNCTION;
206 if (table->s->fields >= 4) // New func table
207 udftype=(Item_udftype) table->field[3]->val_int();
208
209 /*
210 Ensure that the .dll doesn't have a path
211 This is done to ensure that only approved dll from the system
212 directories are used (to make this even remotely secure).
213
214 On windows we must check both FN_LIBCHAR and '/'.
215 */
216
217 LEX_CSTRING name_cstr= {name.str, name.length};
218 if (check_valid_path(dl_name, strlen(dl_name)) ||
219 check_string_char_length(name_cstr, "", NAME_CHAR_LEN,
220 system_charset_info, 1))
221 {
222 sql_print_error("Invalid row in mysql.func table for function '%.64s'",
223 name.str);
224 continue;
225 }
226
227 if (!(tmp= add_udf(&name,(Item_result) table->field[1]->val_int(),
228 dl_name, udftype)))
229 {
230 sql_print_error("Can't alloc memory for udf function: '%.64s'", name.str);
231 continue;
232 }
233
234 void *dl = find_udf_dl(tmp->dl);
235 if (dl == NULL)
236 {
237 char dlpath[FN_REFLEN];
238 strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", tmp->dl,
239 NullS);
240 (void) unpack_filename(dlpath, dlpath);
241 if (!(dl= dlopen(dlpath, RTLD_NOW)))
242 {
243 const char *errmsg;
244 int error_number= dlopen_errno;
245 DLERROR_GENERATE(errmsg, error_number);
246
247 /* Print warning to log */
248 sql_print_error(ER(ER_CANT_OPEN_LIBRARY), tmp->dl, error_number, errmsg);
249 /* Keep the udf in the hash so that we can remove it later */
250 continue;
251 }
252 new_dl=1;
253 }
254 tmp->dlhandle = dl;
255 {
256 char buf[NAME_LEN+16], *missing;
257 if ((missing= init_syms(tmp, buf)))
258 {
259 sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), missing);
260 del_udf(tmp);
261 if (new_dl)
262 dlclose(dl);
263 }
264 }
265 }
266 if (error > 0)
267 sql_print_error("Got unknown error: %d", my_errno());
268 end_read_record(&read_record_info);
269 table->m_needs_reopen= TRUE; // Force close to free memory
270
271 end:
272 close_mysql_tables(new_thd);
273 delete new_thd;
274 DBUG_VOID_RETURN;
275 }
276
277
udf_free()278 void udf_free()
279 {
280 /* close all shared libraries */
281 DBUG_ENTER("udf_free");
282 for (uint idx=0 ; idx < udf_hash.records ; idx++)
283 {
284 udf_func *udf=(udf_func*) my_hash_element(&udf_hash,idx);
285 if (udf->dlhandle) // Not closed before
286 {
287 /* Mark all versions using the same handler as closed */
288 for (uint j=idx+1 ; j < udf_hash.records ; j++)
289 {
290 udf_func *tmp=(udf_func*) my_hash_element(&udf_hash,j);
291 if (udf->dlhandle == tmp->dlhandle)
292 tmp->dlhandle=0; // Already closed
293 }
294 dlclose(udf->dlhandle);
295 }
296 }
297 my_hash_free(&udf_hash);
298 free_root(&mem,MYF(0));
299 if (initialized)
300 {
301 initialized= 0;
302 mysql_rwlock_destroy(&THR_LOCK_udf);
303 }
304 DBUG_VOID_RETURN;
305 }
306
307
del_udf(udf_func * udf)308 static void del_udf(udf_func *udf)
309 {
310 DBUG_ENTER("del_udf");
311 if (!--udf->usage_count)
312 {
313 my_hash_delete(&udf_hash,(uchar*) udf);
314 using_udf_functions=udf_hash.records != 0;
315 }
316 else
317 {
318 /*
319 The functions is in use ; Rename the functions instead of removing it.
320 The functions will be automaticly removed when the least threads
321 doesn't use it anymore
322 */
323 char *name= udf->name.str;
324 size_t name_length=udf->name.length;
325 udf->name.str=(char*) "*";
326 udf->name.length=1;
327 my_hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length);
328 }
329 DBUG_VOID_RETURN;
330 }
331
332
free_udf(udf_func * udf)333 void free_udf(udf_func *udf)
334 {
335 DBUG_ENTER("free_udf");
336
337 if (!initialized)
338 DBUG_VOID_RETURN;
339
340 mysql_rwlock_wrlock(&THR_LOCK_udf);
341 if (!--udf->usage_count)
342 {
343 /*
344 We come here when someone has deleted the udf function
345 while another thread still was using the udf
346 */
347 my_hash_delete(&udf_hash,(uchar*) udf);
348 using_udf_functions=udf_hash.records != 0;
349 if (!find_udf_dl(udf->dl))
350 dlclose(udf->dlhandle);
351 }
352 mysql_rwlock_unlock(&THR_LOCK_udf);
353 DBUG_VOID_RETURN;
354 }
355
356
357 /* This is only called if using_udf_functions != 0 */
358
find_udf(const char * name,size_t length,bool mark_used)359 udf_func *find_udf(const char *name, size_t length,bool mark_used)
360 {
361 udf_func *udf=0;
362 DBUG_ENTER("find_udf");
363
364 if (!initialized)
365 DBUG_RETURN(NULL);
366
367 /* TODO: This should be changed to reader locks someday! */
368 if (mark_used)
369 mysql_rwlock_wrlock(&THR_LOCK_udf); /* Called during fix_fields */
370 else
371 mysql_rwlock_rdlock(&THR_LOCK_udf); /* Called during parsing */
372
373 if ((udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) name,
374 length ? length : strlen(name))))
375 {
376 if (!udf->dlhandle)
377 udf=0; // Could not be opened
378 else if (mark_used)
379 udf->usage_count++;
380 }
381 mysql_rwlock_unlock(&THR_LOCK_udf);
382 DBUG_RETURN(udf);
383 }
384
385
find_udf_dl(const char * dl)386 static void *find_udf_dl(const char *dl)
387 {
388 DBUG_ENTER("find_udf_dl");
389
390 /*
391 Because only the function name is hashed, we have to search trough
392 all rows to find the dl.
393 */
394 for (uint idx=0 ; idx < udf_hash.records ; idx++)
395 {
396 udf_func *udf=(udf_func*) my_hash_element(&udf_hash,idx);
397 if (!strcmp(dl, udf->dl) && udf->dlhandle != NULL)
398 DBUG_RETURN(udf->dlhandle);
399 }
400 DBUG_RETURN(0);
401 }
402
403
404 /* Assume that name && dl is already allocated */
405
add_udf(LEX_STRING * name,Item_result ret,char * dl,Item_udftype type)406 static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl,
407 Item_udftype type)
408 {
409 if (!name || !dl || !(uint) type || (uint) type > (uint) UDFTYPE_AGGREGATE)
410 return 0;
411 udf_func *tmp= (udf_func*) alloc_root(&mem, sizeof(udf_func));
412 if (!tmp)
413 return 0;
414 memset(tmp, 0, sizeof(*tmp));
415 tmp->name = *name; //dup !!
416 tmp->dl = dl;
417 tmp->returns = ret;
418 tmp->type = type;
419 tmp->usage_count=1;
420 if (my_hash_insert(&udf_hash,(uchar*) tmp))
421 return 0;
422 using_udf_functions=1;
423 return tmp;
424 }
425
426
427 /**
428 Create a user defined function.
429
430 @note Like implementations of other DDL/DML in MySQL, this function
431 relies on the caller to close the thread tables. This is done in the
432 end of dispatch_command().
433 */
434
mysql_create_function(THD * thd,udf_func * udf)435 int mysql_create_function(THD *thd,udf_func *udf)
436 {
437 int error;
438 void *dl=0;
439 bool new_dl=0;
440 TABLE *table;
441 TABLE_LIST tables;
442 udf_func *u_d;
443 bool save_binlog_row_based;
444 DBUG_ENTER("mysql_create_function");
445
446 if (!initialized)
447 {
448 if (opt_noacl)
449 my_error(ER_CANT_INITIALIZE_UDF, MYF(0),
450 udf->name.str,
451 "UDFs are unavailable with the --skip-grant-tables option");
452 else
453 my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
454 DBUG_RETURN(1);
455 }
456
457 /*
458 Ensure that the .dll doesn't have a path
459 This is done to ensure that only approved dll from the system
460 directories are used (to make this even remotely secure).
461 */
462 if (check_valid_path(udf->dl, strlen(udf->dl)))
463 {
464 my_message(ER_UDF_NO_PATHS, ER(ER_UDF_NO_PATHS), MYF(0));
465 DBUG_RETURN(1);
466 }
467 LEX_CSTRING udf_name_cstr= {udf->name.str, udf->name.length};
468 if (check_string_char_length(udf_name_cstr, "", NAME_CHAR_LEN,
469 system_charset_info, 1))
470 {
471 my_error(ER_TOO_LONG_IDENT, MYF(0), udf->name.str);
472 DBUG_RETURN(1);
473 }
474
475 tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE);
476 if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
477 DBUG_RETURN(1);
478
479 /*
480 Turn off row binlogging of this statement and use statement-based
481 so that all supporting tables are updated for CREATE FUNCTION command.
482 */
483 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
484 thd->clear_current_stmt_binlog_format_row();
485
486 mysql_rwlock_wrlock(&THR_LOCK_udf);
487 if ((my_hash_search(&udf_hash,(uchar*) udf->name.str, udf->name.length)))
488 {
489 my_error(ER_UDF_EXISTS, MYF(0), udf->name.str);
490 goto err;
491 }
492 if (!(dl = find_udf_dl(udf->dl)))
493 {
494 char dlpath[FN_REFLEN];
495 strxnmov(dlpath, sizeof(dlpath) - 1, opt_plugin_dir, "/", udf->dl, NullS);
496 (void) unpack_filename(dlpath, dlpath);
497
498 if (!(dl = dlopen(dlpath, RTLD_NOW)))
499 {
500 const char *errmsg;
501 int error_number= dlopen_errno;
502 DLERROR_GENERATE(errmsg, error_number);
503
504 DBUG_PRINT("error",("dlopen of %s failed, error: %d (%s)",
505 udf->dl, error_number, errmsg));
506 my_error(ER_CANT_OPEN_LIBRARY, MYF(0),
507 udf->dl, error_number, errmsg);
508 goto err;
509 }
510 new_dl=1;
511 }
512 udf->dlhandle=dl;
513 {
514 char buf[NAME_LEN+16], *missing;
515 if ((missing= init_syms(udf, buf)))
516 {
517 my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), missing);
518 goto err;
519 }
520 }
521 udf->name.str=strdup_root(&mem,udf->name.str);
522 udf->dl=strdup_root(&mem,udf->dl);
523 if (!(u_d=add_udf(&udf->name,udf->returns,udf->dl,udf->type)))
524 goto err;
525 u_d->dlhandle = dl;
526 u_d->func=udf->func;
527 u_d->func_init=udf->func_init;
528 u_d->func_deinit=udf->func_deinit;
529 u_d->func_clear=udf->func_clear;
530 u_d->func_add=udf->func_add;
531
532 /* create entry in mysql.func table */
533
534 table->use_all_columns();
535 restore_record(table, s->default_values); // Default values for fields
536 table->field[0]->store(u_d->name.str, u_d->name.length, system_charset_info);
537 table->field[1]->store((longlong) u_d->returns, TRUE);
538 table->field[2]->store(u_d->dl, strlen(u_d->dl), system_charset_info);
539 if (table->s->fields >= 4) // If not old func format
540 table->field[3]->store((longlong) u_d->type, TRUE);
541 error = table->file->ha_write_row(table->record[0]);
542
543 if (error)
544 {
545 char errbuf[MYSYS_STRERROR_SIZE];
546 my_error(ER_ERROR_ON_WRITE, MYF(0), "mysql.func", error,
547 my_strerror(errbuf, sizeof(errbuf), error));
548 del_udf(u_d);
549 goto err;
550 }
551 mysql_rwlock_unlock(&THR_LOCK_udf);
552
553 /* Binlog the create function. */
554 if (write_bin_log(thd, true, thd->query().str, thd->query().length))
555 {
556 /* Restore the state of binlog format */
557 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
558 if (save_binlog_row_based)
559 thd->set_current_stmt_binlog_format_row();
560 DBUG_RETURN(1);
561 }
562 /* Restore the state of binlog format */
563 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
564 if (save_binlog_row_based)
565 thd->set_current_stmt_binlog_format_row();
566 DBUG_RETURN(0);
567
568 err:
569 if (new_dl)
570 dlclose(dl);
571 mysql_rwlock_unlock(&THR_LOCK_udf);
572 /* Restore the state of binlog format */
573 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
574 if (save_binlog_row_based)
575 thd->set_current_stmt_binlog_format_row();
576 DBUG_RETURN(1);
577 }
578
579
mysql_drop_function(THD * thd,const LEX_STRING * udf_name)580 int mysql_drop_function(THD *thd,const LEX_STRING *udf_name)
581 {
582 TABLE *table;
583 TABLE_LIST tables;
584 udf_func *udf;
585 char *exact_name_str;
586 size_t exact_name_len;
587 bool save_binlog_row_based;
588 int error= 1;
589 DBUG_ENTER("mysql_drop_function");
590
591 if (!initialized)
592 {
593 if (opt_noacl)
594 my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
595 else
596 my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
597 DBUG_RETURN(1);
598 }
599
600 tables.init_one_table("mysql", 5, "func", 4, "func", TL_WRITE);
601 if (!(table= open_ltable(thd, &tables, TL_WRITE, MYSQL_LOCK_IGNORE_TIMEOUT)))
602 DBUG_RETURN(1);
603
604 /*
605 Turn off row binlogging of this statement and use statement-based
606 so that all supporting tables are updated for DROP FUNCTION command.
607 */
608 if ((save_binlog_row_based= thd->is_current_stmt_binlog_format_row()))
609 thd->clear_current_stmt_binlog_format_row();
610
611 mysql_rwlock_wrlock(&THR_LOCK_udf);
612 if (!(udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) udf_name->str,
613 (uint) udf_name->length)))
614 {
615 my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), udf_name->str);
616 mysql_rwlock_unlock(&THR_LOCK_udf);
617 goto exit;
618 }
619 exact_name_str= udf->name.str;
620 exact_name_len= udf->name.length;
621 del_udf(udf);
622 /*
623 Close the handle if this was function that was found during boot or
624 CREATE FUNCTION and it's not in use by any other udf function
625 */
626 if (udf->dlhandle && !find_udf_dl(udf->dl))
627 dlclose(udf->dlhandle);
628 mysql_rwlock_unlock(&THR_LOCK_udf);
629
630 table->use_all_columns();
631 table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin);
632 if (!table->file->ha_index_read_idx_map(table->record[0], 0,
633 table->field[0]->ptr,
634 HA_WHOLE_KEY,
635 HA_READ_KEY_EXACT))
636 {
637 int delete_err;
638 if ((delete_err = table->file->ha_delete_row(table->record[0])))
639 table->file->print_error(delete_err, MYF(0));
640 }
641
642 /*
643 Binlog the drop function. Keep the table open and locked
644 while binlogging, to avoid binlog inconsistency.
645 */
646 if (!write_bin_log(thd, true, thd->query().str, thd->query().length))
647 error= 0;
648 exit:
649 /* Restore the state of binlog format */
650 DBUG_ASSERT(!thd->is_current_stmt_binlog_format_row());
651 if (save_binlog_row_based)
652 thd->set_current_stmt_binlog_format_row();
653 DBUG_RETURN(error);
654 }
655
656 #endif /* HAVE_DLOPEN */
657