1 /* Copyright (C) 2015 MariaDB
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 as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
15
16 #include <my_global.h>
17 #include <my_sys.h>
18 #include <my_crypt.h>
19 #include <tap.h>
20
21 /*** tweaks and stubs for encryption code to compile ***************/
22 #define KEY_SIZE (128/8)
23
24 my_bool encrypt_tmp_files;
25 int init_io_cache_encryption();
26
encryption_key_get_latest_version_func(uint)27 uint encryption_key_get_latest_version_func(uint)
28 {
29 return 1;
30 }
31
encryption_key_id_exists_func(uint)32 uint encryption_key_id_exists_func(uint)
33 {
34 return 1;
35 }
36
encryption_key_version_exists_func(uint,uint)37 uint encryption_key_version_exists_func(uint, uint)
38 {
39 return 1;
40 }
41
encryption_key_get_func(uint,uint,uchar * key,uint * size)42 uint encryption_key_get_func(uint, uint, uchar* key, uint* size)
43 {
44 if (*size < KEY_SIZE)
45 {
46 *size= KEY_SIZE;
47 return ENCRYPTION_KEY_BUFFER_TOO_SMALL;
48 }
49 memset(key, KEY_SIZE, *size= KEY_SIZE);
50 return 0;
51 }
52
encryption_ctx_size_func(unsigned int,unsigned int)53 uint encryption_ctx_size_func(unsigned int, unsigned int)
54 {
55 return MY_AES_CTX_SIZE;
56 }
57
58
59 #ifdef HAVE_EncryptAes128Gcm
60 enum my_aes_mode aes_mode= MY_AES_GCM;
61 #else
62 enum my_aes_mode aes_mode= MY_AES_CBC;
63 #endif
64
encryption_ctx_init_func(void * ctx,const unsigned char * key,unsigned int klen,const unsigned char * iv,unsigned int ivlen,int flags,unsigned int key_id,unsigned int key_version)65 int encryption_ctx_init_func(void *ctx, const unsigned char* key, unsigned int klen,
66 const unsigned char* iv, unsigned int ivlen,
67 int flags, unsigned int key_id,
68 unsigned int key_version)
69 {
70 return my_aes_crypt_init(ctx, aes_mode, flags, key, klen, iv, ivlen);
71 }
72
encryption_encrypted_length_func(unsigned int slen,unsigned int key_id,unsigned int key_version)73 uint encryption_encrypted_length_func(unsigned int slen, unsigned int key_id, unsigned int key_version)
74 {
75 return my_aes_get_size(aes_mode, slen);
76 }
77
78 struct encryption_service_st encryption_handler=
79 {
80 encryption_key_get_latest_version_func,
81 encryption_key_get_func,
82 encryption_ctx_size_func,
83 encryption_ctx_init_func,
84 my_aes_crypt_update,
85 my_aes_crypt_finish,
86 encryption_encrypted_length_func
87 };
88
sql_print_information(const char * format,...)89 void sql_print_information(const char *format, ...)
90 {
91 }
92
sql_print_error(const char * format,...)93 void sql_print_error(const char *format, ...)
94 {
95 }
96
97 /*** end of encryption tweaks and stubs ****************************/
98
99 static IO_CACHE info;
100 #define CACHE_SIZE 16384
101
102 #define INFO_TAIL ", pos_in_file = %llu, pos_in_mem = %lu", \
103 info.pos_in_file, (ulong) ((info.type == READ_CACHE ? info.read_pos : info.write_pos) - info.request_pos)
104
105 #define FILL 0x5A
106
data_bad(const uchar * buf,size_t len)107 int data_bad(const uchar *buf, size_t len)
108 {
109 const uchar *end= buf + len;
110 while (buf < end)
111 if (*buf++ != FILL)
112 return 1;
113 return 0;
114 }
115
temp_io_cache()116 void temp_io_cache()
117 {
118 int res;
119 uchar buf[CACHE_SIZE + 200];
120 memset(buf, FILL, sizeof(buf));
121
122 diag("temp io_cache with%s encryption", encrypt_tmp_files?"":"out");
123
124 init_io_cache_encryption();
125
126 res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0);
127 ok(res == 0, "open_cached_file" INFO_TAIL);
128
129 res= my_b_write(&info, buf, 100);
130 ok(res == 0 && info.pos_in_file == 0, "small write" INFO_TAIL );
131
132 res= my_b_write(&info, buf, sizeof(buf));
133 ok(res == 0 && info.pos_in_file == CACHE_SIZE, "large write" INFO_TAIL);
134
135 res= reinit_io_cache(&info, WRITE_CACHE, 250, 0, 0);
136 ok(res == 0, "reinit with rewind" INFO_TAIL);
137
138 res= my_b_write(&info, buf, sizeof(buf));
139 ok(res == 0, "large write" INFO_TAIL);
140
141 res= my_b_flush_io_cache(&info, 1);
142 ok(res == 0, "flush" INFO_TAIL);
143
144 res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0);
145 ok(res == 0, "reinit READ_CACHE" INFO_TAIL);
146
147 res= (int)my_pread(info.file, buf, 50, 50, MYF(MY_NABP));
148 ok(res == 0 && data_bad(buf, 50) == encrypt_tmp_files,
149 "file must be %sreadable", encrypt_tmp_files ?"un":"");
150
151 res= my_b_read(&info, buf, 50) || data_bad(buf, 50);
152 ok(res == 0 && info.pos_in_file == 0, "small read" INFO_TAIL);
153
154 res= my_b_read(&info, buf, sizeof(buf)) || data_bad(buf, sizeof(buf));
155 ok(res == 0 && info.pos_in_file == CACHE_SIZE, "large read" INFO_TAIL);
156
157 close_cached_file(&info);
158 }
159
mdev9044()160 void mdev9044()
161 {
162 int res;
163 uchar buf[CACHE_SIZE + 200];
164
165 diag("MDEV-9044 Binlog corruption in Galera");
166
167 res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0);
168 ok(res == 0, "open_cached_file" INFO_TAIL);
169
170 res= my_b_write(&info, USTRING_WITH_LEN("first write\0"));
171 ok(res == 0, "first write" INFO_TAIL);
172
173 res= my_b_flush_io_cache(&info, 1);
174 ok(res == 0, "flush" INFO_TAIL);
175
176 res= reinit_io_cache(&info, WRITE_CACHE, 0, 0, 0);
177 ok(res == 0, "reinit WRITE_CACHE" INFO_TAIL);
178
179 res= my_b_write(&info, USTRING_WITH_LEN("second write\0"));
180 ok(res == 0, "second write" INFO_TAIL );
181
182 res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0);
183 ok(res == 0, "reinit READ_CACHE" INFO_TAIL);
184
185 res= (int)my_b_fill(&info);
186 ok(res == 0, "fill" INFO_TAIL);
187
188 res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0);
189 ok(res == 0, "reinit READ_CACHE" INFO_TAIL);
190
191 res= my_b_read(&info, buf, sizeof(buf));
192 ok(res == 1 && strcmp((char*)buf, "second write") == 0, "read '%s'", buf);
193
194 close_cached_file(&info);
195 }
196
197 /* 2 Reads (with my_b_fill) in cache makes second read to fail */
mdev10259()198 void mdev10259()
199 {
200 int res;
201 uchar buf[200];
202 memset(buf, FILL, sizeof(buf));
203
204 diag("MDEV-10259- mysqld crash with certain statement length and order with"
205 " Galera and encrypt-tmp-files=1");
206
207 init_io_cache_encryption();
208
209 res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0);
210 ok(res == 0, "open_cached_file" INFO_TAIL);
211
212 res= my_b_write(&info, buf, sizeof(buf));
213 ok(res == 0 && info.pos_in_file == 0, "200 write" INFO_TAIL);
214
215 res= my_b_flush_io_cache(&info, 1);
216 ok(res == 0, "flush" INFO_TAIL);
217
218 my_off_t saved_pos= my_b_tell(&info);
219 res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0);
220 ok(res == 0, "reinit READ_CACHE" INFO_TAIL);
221
222 size_t s= my_b_fill(&info);
223 ok(s == 200, "fill" INFO_TAIL);
224
225 s= my_b_fill(&info);
226 ok(s == 0, "fill" INFO_TAIL);
227
228 s= my_b_fill(&info);
229 ok(s == 0, "fill" INFO_TAIL);
230
231 res= reinit_io_cache(&info, WRITE_CACHE, saved_pos, 0, 0);
232 ok(res == 0, "reinit WRITE_CACHE" INFO_TAIL);
233
234 res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0);
235 ok(res == 0, "reinit READ_CACHE" INFO_TAIL);
236
237 ok(200 == my_b_bytes_in_cache(&info),"my_b_bytes_in_cache == 200");
238
239 s= my_b_fill(&info);
240 ok(s == 0, "fill" INFO_TAIL);
241
242 s= my_b_fill(&info);
243 ok(s == 0, "fill" INFO_TAIL);
244
245 s= my_b_fill(&info);
246 ok(s == 0, "fill" INFO_TAIL);
247
248 res= reinit_io_cache(&info, WRITE_CACHE, saved_pos, 0, 0);
249 ok(res == 0, "reinit WRITE_CACHE" INFO_TAIL);
250
251 res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0);
252 ok(res == 0, "reinit READ_CACHE" INFO_TAIL);
253
254 ok(200 == my_b_bytes_in_cache(&info),"my_b_bytes_in_cache == 200");
255
256 res= my_b_read(&info, buf, sizeof(buf)) || data_bad(buf, sizeof(buf));
257 ok(res == 0 && info.pos_in_file == 0, "large read" INFO_TAIL);
258
259 close_cached_file(&info);
260
261 }
262
mdev14014()263 void mdev14014()
264 {
265 int res;
266 uchar buf_o[200];
267 uchar buf_i[200];
268 memset(buf_i, 0, sizeof( buf_i));
269 memset(buf_o, FILL, sizeof(buf_o));
270
271 diag("MDEV-14014 Dump thread reads past last 'officially' written byte");
272
273 init_io_cache_encryption();
274
275 res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0);
276 ok(res == 0, "open_cached_file" INFO_TAIL);
277
278 res= my_b_write(&info, buf_o, sizeof(buf_o));
279 ok(res == 0, "buffer is written" INFO_TAIL);
280
281 res= my_b_flush_io_cache(&info, 1);
282 ok(res == 0, "flush" INFO_TAIL);
283
284 res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0);
285 ok(res == 0, "reinit READ_CACHE" INFO_TAIL);
286
287 info.end_of_file= 100;
288 res= my_b_read(&info, buf_i, sizeof(buf_i));
289 ok(res == 1 && buf_i[100] == 0 && buf_i[200-1] == 0,
290 "short read leaves buf_i[100..200-1] == 0");
291
292 close_cached_file(&info);
293 }
294
mdev17133()295 void mdev17133()
296 {
297 my_off_t res;
298 int k;
299 const int eof_iter=4, read_iter= 4;
300 uchar buf_i[1024*256]; // read
301 uchar buf_o[sizeof(buf_i)]; // write
302 const size_t eof_block_size= sizeof(buf_o) / eof_iter;
303 const size_t read_size= eof_block_size / read_iter;
304 size_t total;
305
306 srand((uint) time(NULL));
307 memset(buf_i, 0, sizeof( buf_i));
308 memset(buf_o, FILL, sizeof(buf_o));
309
310 diag("MDEV-17133 Dump thread reads from the past");
311
312 init_io_cache_encryption();
313
314 res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0);
315 ok(res == 0, "open_cached_file" INFO_TAIL);
316
317 res= my_b_write(&info, buf_o, sizeof(buf_o));
318 ok(res == 0, "buffer is written" INFO_TAIL);
319 res= my_b_tell(&info);
320 ok(res == sizeof(buf_o), "cache size as expected");
321
322 res= my_b_flush_io_cache(&info, 1);
323 ok(res == 0, "flush" INFO_TAIL);
324 res= reinit_io_cache(&info, READ_CACHE, 0, 0, 0);
325 ok(res == 0, "reinit READ_CACHE" INFO_TAIL);
326
327 // read the written data by chunks of variable size eof_iter times
328 for (k= eof_iter, info.end_of_file=0, total= 0; k; k--)
329 {
330 int i;
331 size_t curr_read_size;
332 info.end_of_file=
333 k == 1 ? sizeof(buf_o) :
334 MY_MIN(sizeof(buf_o),
335 info.end_of_file + eof_block_size +
336 // plus 25% of block for randomization to the average
337 eof_block_size/4 - rand() % (eof_block_size/2));
338
339 // read a chunk by blocks of variable size read_iter times
340 // the last block completes the current chunk
341 for (i= 0; i < read_iter; i++, total += curr_read_size)
342 {
343 char buf_check[eof_block_size];
344 size_t a,b;
345
346 a= (size_t)(info.end_of_file - total);
347 b= read_size + read_size/4 - rand() % (read_size/2);
348 curr_read_size= (i == read_iter - 1) ? a :
349 MY_MIN(a, b);
350
351 DBUG_ASSERT(curr_read_size <= info.end_of_file - total);
352
353 res= my_b_read(&info, buf_i + total, MY_MIN(19, curr_read_size));
354 ok(res == 0, "read of 19");
355 // mark read bytes in the used part of the cache buffer
356 memset(info.buffer, 0, info.read_pos - info.buffer);
357
358 // random size 2nd read
359 res= my_b_read(&info, buf_i + total + MY_MIN(19, curr_read_size),
360 19 >= curr_read_size ? 0 : curr_read_size - 19);
361 ok(res == 0, "rest of read %zu", curr_read_size - 19);
362 // mark read bytes in the used part of the cache buffer
363 memset(info.buffer, 0, info.read_pos - info.buffer);
364
365 // check that no marked bytes are read
366 memset(buf_check, FILL, curr_read_size);
367 ok(memcmp(buf_i + total, buf_check, curr_read_size) == 0,
368 "read correct data");
369 }
370 ok(info.pos_in_file + (info.read_end - info.buffer) == info.end_of_file,
371 "cache is read up to eof");
372 ok(total == info.end_of_file, "total matches eof");
373 }
374 ok(total == sizeof(buf_i), "read total size match");
375 ok(buf_i[sizeof(buf_i) - 1] == FILL, "data read correctly");
376
377 close_cached_file(&info);
378 }
379
380
mdev10963()381 void mdev10963()
382 {
383 int res;
384 uint n_checks= 8;
385 uchar buf[1024 * 512];
386 uint n_frag= sizeof(buf)/(2 * CACHE_SIZE);
387 FILE *file;
388 myf my_flags= MYF(MY_WME);
389 const char *file_name="cache.log";
390
391 memset(buf, FILL, sizeof(buf));
392 diag("MDEV-10963 Fragmented BINLOG query");
393
394 init_io_cache_encryption();
395 srand((uint) time(NULL));
396
397 /* copying source */
398 res= open_cached_file(&info, 0, 0, CACHE_SIZE, 0);
399 ok(res == 0, "open_cached_file" INFO_TAIL);
400 res= my_b_write(&info, buf, sizeof(buf));
401
402 ulonglong total_size= my_b_tell(&info);
403 ok(res == 0 && total_size == sizeof(buf), "cache is written");
404
405 /* destination */
406 file= my_fopen(file_name, O_RDWR | O_TRUNC | O_CREAT, my_flags);
407 ok(my_fileno(file) > 0, "opened file fd = %d", my_fileno(file));
408
409 /*
410 For n_checks times verify a sequence of copying with random fragment
411 size ranging from zero to about the double of the cache read buffer size.
412 */
413 for (; n_checks; n_checks--, rewind(file))
414 {
415 // copied size is an estimate can be incremeneted to greater than total_size
416 ulong copied_size= 0;
417
418 res= reinit_io_cache(&info, READ_CACHE, 0L, FALSE, FALSE);
419 ok(res == 0, "cache turned to read");
420
421 for (ulong i= 0, curr_size= 0; i < n_frag; i++, copied_size += curr_size)
422 {
423 curr_size= rand() % (2 * (total_size - copied_size) / (n_frag - i));
424
425 DBUG_ASSERT(curr_size <= total_size - copied_size || i == n_frag - 1);
426
427 res= my_b_copy_to_file(&info, file, curr_size);
428 ok(res == 0, "%lu of the cache copied to file", curr_size);
429 }
430 /*
431 Regardless of total_size <> copied_size the function succeeds:
432 when total_size < copied_size the huge overflowed value of the last
433 argument is ignored because nothing already left uncopied in the cache.
434 */
435 res= my_b_copy_to_file(&info, file, (size_t) total_size - copied_size);
436 ok(res == 0, "%llu of the cache copied to file", total_size - copied_size);
437 ok(my_ftell(file, my_flags) == sizeof(buf),
438 "file written in %d fragments", n_frag+1);
439
440 res= reinit_io_cache(&info, WRITE_CACHE, total_size, 0, 0);
441 ok(res == 0 && my_b_tell(&info) == sizeof(buf), "cache turned to write");
442 }
443 close_cached_file(&info);
444 my_fclose(file, my_flags);
445 my_delete(file_name, MYF(MY_WME));
446 }
447
main(int argc,char * argv[])448 int main(int argc __attribute__((unused)),char *argv[])
449 {
450 MY_INIT(argv[0]);
451 plan(277);
452
453 /* temp files with and without encryption */
454 encrypt_tmp_files= 1;
455 temp_io_cache();
456
457 encrypt_tmp_files= 0;
458 temp_io_cache();
459
460 /* regression tests */
461 mdev9044();
462
463 encrypt_tmp_files= 1;
464 mdev10259();
465 encrypt_tmp_files= 0;
466
467 mdev14014();
468 mdev17133();
469 mdev10963();
470
471 my_end(0);
472 return exit_status();
473 }
474
475