5454#include <m_string.h>
5555#include <m_ctype.h>
5656#include <hash.h>
57+ #include <ctype.h>
5758#include <stdarg.h>
5859
5960#include "client_priv.h"
@@ -2328,6 +2329,82 @@ static char const* fix_identifier_with_newline(char const* object_name, my_bool*
23282329}
23292330
23302331
2332+ /*
2333+ * fprintf_string:
2334+ * -- Print the escaped version of the given char* row into the md_result_file.
2335+ *
2336+ * @param[in] row the row to be printed
2337+ * @param[in] row_len length of the row
2338+ * @param[in] quote quote character, like ' or ` etc.
2339+ * @param[in] needs_newline whether to print newline after the row
2340+ *
2341+ * @retval void
2342+ *
2343+ */
2344+ static void fprintf_string (char * row , ulong row_len , char quote ,
2345+ my_bool needs_newline ) {
2346+ // Create the buffer where we'll have sanitized row.
2347+ char buffer [2048 ];
2348+ char * pbuffer ;
2349+ uint64_t curr_row_size ;
2350+ pbuffer = & buffer [0 ];
2351+
2352+ curr_row_size = ((uint64_t )row_len ) * 2 + 1 ;
2353+
2354+ // We'll allocate dynamic memory only for huge rows
2355+ if (curr_row_size > sizeof (buffer ))
2356+ pbuffer = (char * )my_malloc (PSI_NOT_INSTRUMENTED , curr_row_size , MYF (0 ));
2357+
2358+ // Put the sanitized row in the buffer.
2359+ mysql_real_escape_string_quote (mysql , pbuffer , row , row_len , '\'' );
2360+
2361+ // Opening quote
2362+ fputc (quote , md_result_file );
2363+
2364+ // Print the row to the file.
2365+ fputs (pbuffer , md_result_file );
2366+
2367+ // Closing quote
2368+ fputc (quote , md_result_file );
2369+
2370+ // Add the new line
2371+ if (needs_newline ) fputc ('\n' , md_result_file );
2372+
2373+ // Free the buffer if we have to.
2374+ if (pbuffer != & buffer [0 ]) my_free (pbuffer );
2375+ }
2376+
2377+ /*
2378+ * is_string_integer:
2379+ * Check if the given string is a valid integer or not.
2380+ *
2381+ * @param[in] str number to be checked
2382+ * @param[in] str_len length of the string
2383+ *
2384+ * @retval TRUE if the string represents an integer
2385+ * @retval FALSE if the string has non-digit characters
2386+ */
2387+ static my_bool is_string_integer (const char * str , ulong str_len ) {
2388+ ulong start_index ;
2389+ ulong i ;
2390+
2391+ // Empty strings are invalid numbers
2392+ if (str_len == 0 ) return FALSE;
2393+
2394+ start_index = 0 ;
2395+
2396+ // For negative integers, start the index with 1
2397+ if (str [0 ] == '-' ) {
2398+ if (str_len == 1 ) return FALSE;
2399+ start_index = 1 ;
2400+ }
2401+
2402+ for (i = start_index ; i < str_len ; i ++ )
2403+ if (!isdigit (str [i ])) return FALSE;
2404+
2405+ return TRUE;
2406+ }
2407+
23312408/*
23322409 create_delimiter
23332410 Generate a new (null-terminated) string that does not exist in query
@@ -4440,6 +4517,7 @@ static int dump_tablespaces(char* ts_where)
44404517 MYSQL_RES * tableres ;
44414518 char buf [FN_REFLEN ];
44424519 DYNAMIC_STRING sqlbuf ;
4520+ ulong * lengths ;
44434521 int first = 0 ;
44444522 /*
44454523 The following are used for parsing the EXTRA field
@@ -4500,24 +4578,33 @@ static int dump_tablespaces(char* ts_where)
45004578 buf [0 ]= 0 ;
45014579 while ((row = mysql_fetch_row (tableres )))
45024580 {
4581+ lengths = mysql_fetch_lengths (tableres );
45034582 if (strcmp (buf , row [0 ]) != 0 )
45044583 first = 1 ;
45054584 if (first )
45064585 {
4586+ /*
4587+ * The print_comment below prints single line comments in the
4588+ * md_result_file (--). The single line comment is terminated by a new
4589+ * line, however because of the usage of mysql_real_escape_string_quote,
4590+ * the new line character will get escaped too in the string, hence
4591+ * another new line characters are being used at the end of the string
4592+ * to terminate the single line comment.
4593+ */
4594+ mysql_real_escape_string_quote (mysql , buf , row [0 ], lengths [0 ], '\'' );
45074595 print_comment (md_result_file , 0 , "\n--\n-- Logfile group: %s\n--\n" ,
4508- row [ 0 ] );
4509-
4596+ buf );
4597+ buf [ 0 ] = 0 ;
45104598 fprintf (md_result_file , "\nCREATE" );
45114599 }
45124600 else
45134601 {
45144602 fprintf (md_result_file , "\nALTER" );
45154603 }
4516- fprintf (md_result_file ,
4517- " LOGFILE GROUP %s\n"
4518- " ADD UNDOFILE '%s'\n" ,
4519- row [0 ],
4520- row [1 ]);
4604+ fprintf (md_result_file , " LOGFILE GROUP " );
4605+ fprintf_string (row [0 ], lengths [0 ], '`' , TRUE);
4606+ fprintf (md_result_file , " ADD UNDOFILE " );
4607+ fprintf_string (row [1 ], lengths [1 ], '\'' , TRUE);
45214608 if (first )
45224609 {
45234610 ubs = strstr (row [5 ],extra_format );
@@ -4527,15 +4614,15 @@ static int dump_tablespaces(char* ts_where)
45274614 endsemi = strstr (ubs ,";" );
45284615 if (endsemi )
45294616 endsemi [0 ]= '\0' ;
4617+ if (!is_string_integer (ubs , (ulong )strlen (ubs ))) return 1 ;
45304618 fprintf (md_result_file ,
45314619 " UNDO_BUFFER_SIZE %s\n" ,
45324620 ubs );
45334621 }
4534- fprintf (md_result_file ,
4535- " INITIAL_SIZE %s\n"
4536- " ENGINE=%s;\n" ,
4537- row [3 ],
4538- row [4 ]);
4622+ if (!is_string_integer (row [3 ], lengths [3 ])) return 1 ;
4623+ fprintf (md_result_file , " INITIAL_SIZE %s\n ENGINE=" , row [3 ]);
4624+ fprintf_string (row [4 ], lengths [4 ], '`' , FALSE);
4625+ fprintf (md_result_file , ";\n" );
45394626 check_io (md_result_file );
45404627 if (first )
45414628 {
@@ -4567,38 +4654,54 @@ static int dump_tablespaces(char* ts_where)
45674654 DBUG_RETURN (1 );
45684655 }
45694656
4657+ DBUG_EXECUTE_IF ("tablespace_injection_test" , {
4658+ mysql_free_result (tableres );
4659+ mysql_query_with_error_report (
4660+ mysql , & tableres ,
4661+ "SELECT 'TN; /*' AS TABLESPACE_NAME, 'FN' AS FILE_NAME, 'LGN' AS "
4662+ "LOGFILE_GROUP_NAME, 77 AS EXTENT_SIZE, 88 AS INITIAL_SIZE, "
4663+ "'*/\nsystem touch foo;\n' AS ENGINE" );
4664+ });
4665+
45704666 buf [0 ]= 0 ;
45714667 while ((row = mysql_fetch_row (tableres )))
45724668 {
4669+ lengths = mysql_fetch_lengths (tableres );
45734670 if (strcmp (buf , row [0 ]) != 0 )
45744671 first = 1 ;
45754672 if (first )
45764673 {
4577- print_comment (md_result_file , 0 , "\n--\n-- Tablespace: %s\n--\n" , row [0 ]);
4674+ /*
4675+ * The print_comment below prints single line comments in the
4676+ * md_result_file (--). The single line comment is terminated by a new
4677+ * line, however because of the usage of mysql_real_escape_string_quote,
4678+ * the new line character will get escaped too in the string, hence
4679+ * another new line characters are being used at the end of the string
4680+ * to terminate the single line comment.
4681+ */
4682+ mysql_real_escape_string_quote (mysql , buf , row [0 ], lengths [0 ], '\'' );
4683+ print_comment (md_result_file , 0 , "\n--\n-- Tablespace: %s\n--\n" , buf );
4684+ buf [0 ] = 0 ;
45784685 fprintf (md_result_file , "\nCREATE" );
45794686 }
45804687 else
45814688 {
45824689 fprintf (md_result_file , "\nALTER" );
45834690 }
4584- fprintf (md_result_file ,
4585- " TABLESPACE %s\n"
4586- " ADD DATAFILE '%s'\n" ,
4587- row [0 ],
4588- row [1 ]);
4589- if (first )
4590- {
4591- fprintf (md_result_file ,
4592- " USE LOGFILE GROUP %s\n"
4593- " EXTENT_SIZE %s\n" ,
4594- row [2 ],
4595- row [3 ]);
4691+ fprintf (md_result_file , " TABLESPACE " );
4692+ fprintf_string (row [0 ], lengths [0 ], '`' , TRUE);
4693+ fprintf (md_result_file , " ADD DATAFILE " );
4694+ fprintf_string (row [1 ], lengths [1 ], '\'' , TRUE);
4695+ if (first ) {
4696+ fprintf (md_result_file , " USE LOGFILE GROUP " );
4697+ fprintf_string (row [2 ], lengths [2 ], '`' , TRUE);
4698+ if (!is_string_integer (row [3 ], lengths [3 ])) return 1 ;
4699+ fprintf (md_result_file , " EXTENT_SIZE %s\n" , row [3 ]);
45964700 }
4597- fprintf (md_result_file ,
4598- " INITIAL_SIZE %s\n"
4599- " ENGINE=%s;\n" ,
4600- row [4 ],
4601- row [5 ]);
4701+ if (!is_string_integer (row [4 ], lengths [4 ])) return 1 ;
4702+ fprintf (md_result_file , " INITIAL_SIZE %s\n ENGINE=" , row [4 ]);
4703+ fprintf_string (row [5 ], lengths [5 ], '`' , FALSE);
4704+ fprintf (md_result_file , ";\n" );
46024705 check_io (md_result_file );
46034706 if (first )
46044707 {
0 commit comments