From: Dmitry Shulga Date: February 9 2011 1:36pm Subject: bzr commit into mysql-5.1-bugteam branch (Dmitry.Shulga:3537) Bug#56976 List-Archive: http://lists.mysql.com/commits/130868 X-Bug: 56976 Message-Id: <201102091337.p19DbctA029160@acsinet15.oracle.com> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============8429815862399578234==" --===============8429815862399578234== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///Users/shulga/projects/mysql/5.1-bugteam-bug56976/ based on revid:dao-gang.qu@stripped 3537 Dmitry Shulga 2011-02-09 Fixed the bug#56976 - Severe Denial Of Service in prepared statements. The problem was that server didn't check resulting size of prepared statement argument which was set using mysql_send_long_data() API. By calling mysql_send_long_data() several times it was possible to create overly big string and thus force server to allocate memory for it. There was no way to limit this allocation. The solution is to add check for size of result string against value of max_long_data_size constant. When intermediate string exceeds max_long_data_size value ER_UNKNOWN_ERROR error is emitted. @ sql/item.cc Added call to my_message when accumulated string exceeds max_long_data_size value. my_messge() calls error handler that was installed in mysql_stmt_get_longdata before call to Item_param::set_longdata. The error handler sets to corresponding value the next current statement fileds: state, last_error, last_errno. @ sql/item.h Added argument of type THD* to declaration of set_longdata(). @ sql/mysqld.cc Added parameter max_long_data_size. This parameter used to limit size of BLOB data that send from client to server over call to mysql_send_long_data() API. @ sql/set_var.cc Added variable 'max_long_data_size' into list of variables displayed by command 'show variables'. @ sql/sql_prepare.cc Added error handler class Set_longdata_error_handler. This handler is installed inside mysql_stmt_get_longdata() before call to param->set_longdata and deinstalled after return from it. Sourcecode snippet that makes checking for statement's state during statement execution moved from Prepared_statement::execute() to Prepared_statement::execute_loop() in order not to call set_parameters() when statement has been failed during set_long_data() execution. If it didn't do then call to set_parameters would failed. @ tests/mysql_client_test.c It was added testcase for the bug #56976. modified: sql/item.cc sql/item.h sql/mysqld.cc sql/set_var.cc sql/sql_class.h sql/sql_prepare.cc tests/mysql_client_test.c === modified file 'sql/item.cc' --- a/sql/item.cc 2010-12-21 11:34:11 +0000 +++ b/sql/item.cc 2011-02-09 13:36:42 +0000 @@ -2729,7 +2729,7 @@ bool Item_param::set_str(const char *str } -bool Item_param::set_longdata(const char *str, ulong length) +bool Item_param::set_longdata(THD *thd, const char *str, ulong length) { DBUG_ENTER("Item_param::set_longdata"); @@ -2742,6 +2742,12 @@ bool Item_param::set_longdata(const char (here), and first have to concatenate all pieces together, write query to the binary log and only then perform conversion. */ + if (str_value.length() + length > thd->variables.max_long_data_size) + DBUG_RETURN(my_message(ER_UNKNOWN_ERROR, + "Result string is longer than " + "'max_long_data_size' bytes", + MYF(0))); + if (str_value.append(str, length, &my_charset_bin)) DBUG_RETURN(TRUE); state= LONG_DATA_VALUE; === modified file 'sql/item.h' --- a/sql/item.h 2010-12-28 23:47:05 +0000 +++ b/sql/item.h 2011-02-09 13:36:42 +0000 @@ -1687,7 +1687,7 @@ public: void set_double(double i); void set_decimal(const char *str, ulong length); bool set_str(const char *str, ulong length); - bool set_longdata(const char *str, ulong length); + bool set_longdata(THD *thd, const char *str, ulong length); void set_time(MYSQL_TIME *tm, timestamp_type type, uint32 max_length_arg); bool set_from_user_var(THD *thd, const user_var_entry *entry); void reset(); === modified file 'sql/mysqld.cc' --- a/sql/mysqld.cc 2010-12-28 23:47:05 +0000 +++ b/sql/mysqld.cc 2011-02-09 13:36:42 +0000 @@ -5687,7 +5687,7 @@ enum options_mysqld OPT_KEY_BUFFER_SIZE, OPT_KEY_CACHE_BLOCK_SIZE, OPT_KEY_CACHE_DIVISION_LIMIT, OPT_KEY_CACHE_AGE_THRESHOLD, OPT_LONG_QUERY_TIME, - OPT_LOWER_CASE_TABLE_NAMES, OPT_MAX_ALLOWED_PACKET, + OPT_LOWER_CASE_TABLE_NAMES, OPT_MAX_ALLOWED_PACKET, OPT_MAX_LONG_DATA_SIZE, OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE, OPT_MAX_CONNECTIONS, OPT_MAX_CONNECT_ERRORS, OPT_MAX_DELAYED_THREADS, OPT_MAX_HEP_TABLE_SIZE, @@ -6862,6 +6862,12 @@ thread is in the relay logs.", &global_system_variables.max_length_for_sort_data, &max_system_variables.max_length_for_sort_data, 0, GET_ULONG, REQUIRED_ARG, 1024, 4, 8192*1024L, 0, 1, 0}, + {"max_long_data_size", OPT_MAX_LONG_DATA_SIZE, + "The maximum BLOB length to send to server from mysql_send_long_data API. " + "Deprecated option; use max_allowed_packet instead.", + &global_system_variables.max_long_data_size, + &max_system_variables.max_long_data_size, 0, GET_ULONG, + REQUIRED_ARG, 1024*1024L, 1024, 0xFFFFFFFF, MALLOC_OVERHEAD, 1, 0}, {"max_prepared_stmt_count", OPT_MAX_PREPARED_STMT_COUNT, "Maximum number of prepared statements in the server.", &max_prepared_stmt_count, &max_prepared_stmt_count, === modified file 'sql/set_var.cc' --- a/sql/set_var.cc 2010-12-28 23:47:05 +0000 +++ b/sql/set_var.cc 2011-02-09 13:36:42 +0000 @@ -394,6 +394,8 @@ static sys_var_thd_ulong sys_max_seeks_f &SV::max_seeks_for_key); static sys_var_thd_ulong sys_max_length_for_sort_data(&vars, "max_length_for_sort_data", &SV::max_length_for_sort_data); +static sys_var_thd_ulong_session_readonly sys_max_long_data_size(&vars, "max_long_data_size", + &SV::max_long_data_size); #ifndef TO_BE_DELETED /* Alias for max_join_size */ static sys_var_thd_ha_rows sys_sql_max_join_size(&vars, "sql_max_join_size", &SV::max_join_size, === modified file 'sql/sql_class.h' --- a/sql/sql_class.h 2010-12-28 23:47:05 +0000 +++ b/sql/sql_class.h 2011-02-09 13:36:42 +0000 @@ -308,6 +308,7 @@ struct system_variables ulong max_allowed_packet; ulong max_error_count; ulong max_length_for_sort_data; + ulong max_long_data_size; ulong max_sort_length; ulong max_tmp_tables; ulong max_insert_delayed_threads; === modified file 'sql/sql_prepare.cc' --- a/sql/sql_prepare.cc 2010-12-28 23:47:05 +0000 +++ b/sql/sql_prepare.cc 2011-02-09 13:36:42 +0000 @@ -95,6 +95,7 @@ When one supplies long data for a placeh #else #include #endif +#include /** A result class used to send cursor rows using the binary protocol. @@ -2730,6 +2731,32 @@ void mysql_sql_stmt_close(THD *thd) } } + +class Set_longdata_error_handler : public Internal_error_handler +{ +public: + Set_longdata_error_handler(Prepared_statement *statement) + :stmt(statement) + { } + +public: + bool handle_error(uint sql_errno, + const char *message, + MYSQL_ERROR::enum_warning_level level, + THD *thd) + { + stmt->state= Query_arena::ERROR; + stmt->last_errno= sql_errno; + strncpy(stmt->last_error, message, MYSQL_ERRMSG_SIZE); + + return TRUE; + } + +private: + Prepared_statement *stmt; +}; + + /** Handle long data in pieces from client. @@ -2786,16 +2813,14 @@ void mysql_stmt_get_longdata(THD *thd, c param= stmt->param_array[param_number]; + Set_longdata_error_handler err_handler(stmt); + thd->push_internal_handler(&err_handler); #ifndef EMBEDDED_LIBRARY - if (param->set_longdata(packet, (ulong) (packet_end - packet))) + param->set_longdata(thd, packet, (ulong) (packet_end - packet)); #else - if (param->set_longdata(thd->extra_data, thd->extra_length)) + param->set_longdata(thd, thd->extra_data, thd->extra_length); #endif - { - stmt->state= Query_arena::ERROR; - stmt->last_errno= ER_OUTOFMEMORY; - sprintf(stmt->last_error, ER(ER_OUTOFMEMORY), 0); - } + thd->pop_internal_handler(); general_log_print(thd, thd->command, NullS); @@ -3257,6 +3282,13 @@ Prepared_statement::execute_loop(String bool error; int reprepare_attempt= 0; + /* Check if we got an error when sending long data */ + if (state == Query_arena::ERROR) + { + my_message(last_errno, last_error, MYF(0)); + return TRUE; + } + if (set_parameters(expanded_query, packet, packet_end)) return TRUE; @@ -3497,12 +3529,6 @@ bool Prepared_statement::execute(String status_var_increment(thd->status_var.com_stmt_execute); - /* Check if we got an error when sending long data */ - if (state == Query_arena::ERROR) - { - my_message(last_errno, last_error, MYF(0)); - return TRUE; - } if (flags & (uint) IS_IN_USE) { my_error(ER_PS_NO_RECURSION, MYF(0)); === modified file 'tests/mysql_client_test.c' --- a/tests/mysql_client_test.c 2010-12-28 23:47:05 +0000 +++ b/tests/mysql_client_test.c 2011-02-09 13:36:42 +0000 @@ -18399,6 +18399,54 @@ static void test_bug47485() /* + Bug #56976: Severe Denial Of Service in prepared statements +*/ + +static void test_bug56976() +{ + MYSQL_STMT *stmt; + MYSQL_BIND bind[1]; + int rc; + const char* query = "SELECT LENGTH(?)"; + char *long_buffer; + unsigned long i, packet_len = 256 * 1024L; + unsigned long dos_len = 2 * 1024 * 1024L; + + DBUG_ENTER("test_bug56976"); + myheader("test_bug56976"); + + stmt= mysql_stmt_init(mysql); + check_stmt(stmt); + + rc= mysql_stmt_prepare(stmt, query, strlen(query)); + check_execute(stmt, rc); + + memset(bind, 0, sizeof(bind)); + bind[0].buffer_type = MYSQL_TYPE_TINY_BLOB; + + rc= mysql_stmt_bind_param(stmt, bind); + check_execute(stmt, rc); + + long_buffer= malloc(packet_len); + memset(long_buffer, 'a', packet_len); + + for (i= 0; i < dos_len / packet_len; i++) + { + rc= mysql_stmt_send_long_data(stmt, 0, long_buffer, packet_len); + check_execute(stmt, rc); + } + + rc= mysql_stmt_execute(stmt); + + DIE_UNLESS(rc && mysql_stmt_errno(stmt) == ER_UNKNOWN_ERROR); + + mysql_stmt_close(stmt); + + DBUG_VOID_RETURN; +} + + +/* Read and parse arguments and MySQL options from my.cnf */ @@ -18724,6 +18772,7 @@ static struct my_tests_st my_tests[]= { { "test_bug42373", test_bug42373 }, { "test_bug54041", test_bug54041 }, { "test_bug47485", test_bug47485 }, + { "test_bug56976", test_bug56976 }, { 0, 0 } }; --===============8429815862399578234== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/dmitry.shulga@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: dmitry.shulga@stripped\ # 70t26hlum9yebx2h # target_branch: file:///Users/shulga/projects/mysql/5.1-bugteam-\ # bug56976/ # testament_sha1: cd5b7cc37e859c67fcc2a9852202361a050dd3af # timestamp: 2011-02-09 19:36:48 +0600 # base_revision_id: dao-gang.qu@stripped\ # 32f4p072xij1zkmj # # Begin bundle IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWUJjd4cACDP/gESQQIBZ//// /+//+r////pgEb1Ohs4s669ub44K+zQqvLc++j3rfO8q7eefWV17jcq232cpyM2tm2VlqloYZICE 0JsmENGiZNJmpgj1NNNNAxB6jRppiMagkoEYCCaT0RT9UGT1PUA0AGjQNAAAABkjJomiNNTE0jTa nlGgABkeo0AAABoAJEQggCZNGTVPyjCU/RpTPVPUxqaek0A9QAAaZBFIgJomTamQp+U9MQT1MU9J mUwjRiBkyaAAAkkBGmgETJgEmp5TRBpp6jNQAAaDQaDTSwUf62l63K5/e20DZG0OfUb4b7rdPVuw ohuagiGvr1Dwx/jq7vt2bW5OM8guawkqKSU5EKIBuwuLXRaLyMSYRXJ0zIx0GDhsyqHSVOmsX7oF 13U25qYzwwCb4SDg0j/jEZeN39sfa0UZgsKbM80rGM6M7vm72ws9M8Wu6O2n5rfP7/VZSXNZmgug WkgNcsQCRPeTvIAKNI/bWOPhKJbAbG0gbEF3Cfxvnt2n96FNO3T17P8Ft3Dh33nTlcG5Y2y9F3cl pS5pNtsbSbTbYDedV2QepJenns04RbexnpptdWvSyHqyLLNIme13NbogJtEzmlNt4TBgZ8mNLmnB halnVtmLLXQWzsoveUqp6VbqnJg73ToXmeiu/tZ8zRAiPRRaFROaF2BvwCJ3cLsBiASj6uZGOrKr RVaX2mofgsjVdEZ552gSSBtfB7Q1KjFxQrSobYtrJCftxvPHGUiVtxcc2siridZbtU/TJ0JyKyG1 I4U4UJu3x2hsvg4xNOI25had+ed32k/EjlTk/PF7KP8LcZNXopNrVoQuZNc/syD1r2IzokzShTwY zBihyiBekmNZKeO2XLHg60R+sssX1jmZyhJYWcjTBRkBNJb10hUdiU7dqkcESklhqxMdbPmuFx01 lZkSCDylcGpYquPbFS+S0QOHQoaN0bnA3ew1adMEe9OBOEVGhG+wm8GW/7Rw+2QR3tCL2wDfs0wI GEFLyiQPcF40HH7YrWB5Dfnp4vk+Jz83fe9eqSX/15Hb8h+y00OrNmKx/Brx2/0tm28Kii8zAN57 A1Vx7pfVlZ7KTUi0J58+Rin23N+jhtxLO6Z6xbLSTyrPXdGVPcpsm+Lvu7mJG/pfFszTLyOOZgyH 1ZdiRiTW+KSD74OeKLZ+NqzZDrj4Vsu/BbwG1wvyuF4YyMJJzAvjdA+QQtMRBSylxfFRLc9bmcry 256h7SH1jvuE5dVxNQxwseOU/swY2HVVrIa2v3Zz5466UF/LA43vdKahSpmZpGhA0gu03Mas6SvY qiD6zh4sDClyW8vZ1pPbt7+D9+RJcBthxiAGyrC5yxiAn1knSxZWpNCTxa/TMwqUiWqQIQj7xsy+ 2SCACZPHApF67kKimdynyFtorBycmqe5MdCURB79iRdfUsUmSootr/uZgehYNnqYDGtIjhjUQXFj CTMF+MsIRaELrYMzZRNskI40HYCDFdGLBIfNM5HHn3VvnIQSr4yukVStYRMFIRkZtq623HrsdSiN McMRBpw1Shndc0EyHXttjpR62zdU25wDMbphDjw5UkETuabmXfnUqG3jSN+D6oGG6XTdLKRE7Hgi R4k+GhbWvU8cm3kIMd1mhBMckLxzJSpqHQz6dIoWWLLp7hZ2TOtqthCac/faRgxJSPUQdiCRRkYi xL3Kt1/DV4iZYcnEy5c1HnBa9HzLLAifGIO5bJyDtDDLHVJjbKxIe+JQ6pmmEjGR3C+FpIGQTDgo 9SEaAiqCCVCdeB4K1KgNa3UvFLI8ldtgQHCcthoLWWMaver/HBXEwcLxZR1s4XZlRA8zkd1O0eVc xAnxMsBYj4l5LvCuA2K85vUcQ/sXJMTVLG1uaXOj90COTHHHU4oLthJXlw6ofWJ6qbz7FhRl/Loh ma3wtJK3B2V86cI2OySKMNDTY4lqUNqORacjwGUlA3rkqYl0rdznxhmPcwXpn57B2mL0tczxzJBW YwOtuEFpOFcVkMD5q++RoSDKLiT3jCGMxjXM4yQa4EzI1KNEHYK4rXvYbTWL1lBIeLoILyBKerGq Vkcx56CZTDaZ4QIWmrtR1SA9zqoIFX5uiUI2ZRhQm8Xbt3u3eeeKC8604GoWELCCkbTQ3Gq9i7LF ZHdQoorUUF+xVq2e9jLQhy5ddWmwNlS+SCiGdL6ClKU2kjm4xKheFjdMG3cZAzljI9B7BUvzRGYW UpExZlRzjBCaCqnRzsFxaE3iC86wrcRsHLqPSHm8iPvtJDkilUGNYUk8rN5iWWjWtnwuYk5nk8hd ovUED9y6/PZdngDtDZyhx2jbeoz0yiLRRNyBipVWBXMjuDF2JYAPAgSBhKMGCgBovdmebRenucEQ 0UhxcA2IDUhNKG7gmXWhv6VGGrmWnxgmrFY4wAg26FdCJJHbESG/onSkoIC5eeU0kLBa3+oe6J7e xTzf931DuDvnm7sHbrw2Srt6Q0wDfI7xMMYRCEBQIqDuiCoTGt7Gsc2PUA1wZkQPHSoX7PZrPgLG xuaPHUu2ybkWI0KPtDFTD4RDqXHVd69YQdFrBFaUZhEkpDbBqxaWueYREgx2DynExdvYZa08Rzf+ RNylnh9UV9bECW7XBioeKfDksA5sqBMU3GieO681YOkFuizR7WIs6JjCmQwTUtRg8qOdLpYr0Rd9 VyD+fpPjOdpp337C6WLiwf1B1cmGmQVGqAi6IqFoQ/YMgchFyg8A5OLXlw1SXiuIo4RgdO/C5vQX VHbBTXu/cfP/MGj4ocC82JR8XRjfk1FQj+iZ8FQYVF9VEoSf1f1kRgURhjvUszCUIWGFggeEgrIr hVwPmxKgkxKIXgIq9FyESS253p4ICX6yR0n2G8Aew3d3wLSszFhP1nvJnt4TGoZcYRAvp9/3Dgen CJWCTxR7ikmzZHCDAR8/3ludCGS2dMWaZhQ8DvoRILsJpMCPXF0mjhcLTpcVAa4uG8TkhHGQgYCg fhEVQmTI2KLwBlQQQsYaNkx8WoVBRaZw4ZqCA3V5e18kfyL9QXgGY7IzGcQAwAapHG0dkUmsZMVg YXBOSMK5URPZs125IA9rM7zgsPpHfdg7ugswhVWSTaZhCZw3AsmEFi5BbnJPQPTL4S3xMxiHQceB 0gS5cSB7dsDEwRE+lMVtNQPTR7dDUAlmZsPDPl1HGJoOMhzLowLdj0oN7NOVlGEIHpT317FUERHY mYWyoSr1fZCKtmhblpy4AdidhhuiDvcMRkQQREa/oqUIGwllRyEFOpNrBv1Xn6xIKyGRQ3IM5fky sMqCvaZL0lKVWw0bF0dPkQ4BpsbQwlxNpmaMcl7gezr+V5ZdtE3S07ETU3HmrbD0A2m5hL6D1AZL dmTwYtT9y0hT4Vmp+vJm25P2ng/gSeUXJc8m4imLtgzGbVia1MNMIpCvijVCjHFHyih4IcyXY4GJ 9PcoeTbYYCetVYZHkc/tKBKhtL74hexekwwUQErxbBKOYjGZ5BGJ8Qhv8m4jMCHD1AhWFjOb5JPP gKxmOdZzc+WRZcy6WhtNjg9M9BySiINSapMVAKgoC7JMRJDWmGo+K6jLSSDlnUrEs1COo8eAEi95 clXwgI2ighKUalJKD1aYQrvatGJe22242RqpSlEdo4wpRVWnQRQNOv9UChDQSeNg/QetGbbsukNu yiFfURDU+93Y2TgGNRhXAyVgNJUHQHiL++7EGecsElwK1yHV6z1SGeqXOKd8nym0kcpz8w6zaVMj Q2nhz1FxR0th3Fx8ZM4zEGJHAGUDamwfwXHqWBtA0qUlUXMiKoxNg+N3NTWi5r7i5KhUYAyZzBNq JjMR5gS7pZo+Fn0QiQz5jnwtPBu7ukyGgWzwzvFkrzzFzLzpePAeDkPek8T9IMAPoaDfZECqiKID 4DZvFv4W/MxZMv4sPjBi3Lzr3CDld09Te9kxCLKHz7M6pmlSMiDmnmPm4x0EY8T3p2jHY+Wdnn4d eqEXVH0FdkoAfbkdl5Al8A01YQij9IrsEMW9dEPZa06PfplXLdoeax4zf0e3OquwYxiF3CWMRxCb 2Se4iqCzs4HzSkyqFPK5vfhd5Bw6DqYsbHY3GK46U2sBJpb2wcw6KkOx0aBJ+V0Qsq4qi1nbiOji Qz0aQwyLYIpIvhVKyYgeIuXD6WPT5u5GvtN4GAIaXNoWdsZTauphhPDBKtGlJTuBJjaO2jBGRZwh Vupb2poDLH+XkBFGhQ8bubqNzijHiIOvV3cV5HNY2DVjAJMIESIIJkJ0HPFQujvPsOjq3sse55ks MJKiqMlxot8bknwtBua7Dj8Z0EFNPiW4W03CjuWN+IGqCJoSBLzdjZ19jh3D2vQneqf8XWuLUbAY e4eoo2oK7SAos6B7PnFbzFmFdYQzFaEvUZs1O0mzbdEk0N3SJUIuqKBjbG2h0TAUNJVAvu4VrdQT vF7CRV0DsDTZgk2liwSXnhkxAPgJNu+4nr8Tq0iGfcGJij0bBooOq+B6pcvCt+FvevO8dgWqudA6 ObEKl9KYGcDXbN1/JbHgwMI+wEYiHxZFJ2C2SVSseGySn9/3einlnnG7zVulIU8jiO/yZ4tNw0xj OxC9kpNJeIqAdymyAtF71yWPVN8s69vS8WOQorc2fdZs+BdwNL5nkvdI7Xjcbu2NjmESs8j3x4Rg dJ2t+qOJaUw1/gCBQDbIcDbG0Nw4TzjlGuFxEyoSOk1UiSiRFX6MMF97Xnf8jwGO8XydeuUGDIBy D6AfQoc+rPApYA1M72Brsy5Qlgm3DWMmi2oeMbaPAF06RdrY23dbMUuiawuCRA4SBwd0x6+/kXQ6 RvxpNHaYZ9Di/NjcQSYqVHqFhyzDo7ArY5bPpNaH1DCH2nX4TVwRJ7tq91jFubrEqhuXE5+YQs2G pt4xKLMLAJGYzwt5JqS1PRERERERERENxht6dYqIrBJcTKuoINETQvBvjWaLniIienNTzbfmb3h+ CMB5AnOplKaR7Ga0tggiIeRgKNiPuoJcjuqTKG9e1r1lrEhx6HKz2isFZPQVm6k+dq0KEbyRywbQ RJC2J6BDkcLTSVNkVpFkSGGhYmiQVCzSUVBBVAmjkhqsT55VU5YiMxYoheGJbIEi/Kl+tg18Uhc3 U8GUd4aQ1anUTTDA9o1Z4PCmYzdRUfGPX2KF1gleaBg2bmLvVBErWoXEaU8QQ98w9MIKHcqrZjYW jSLrV69Cos/FbBUXuMyfJH1wBoq72i8E2ZWq/O12CGfiINFKFhF2UxWilhM1Z4rmLhYqkeDGKTL1 ce/dLFcTRcYMnp15MmJkabr2xs3nDStQ4GVMBOLBqeV0uV6zjEI6NYBW5ChgDL2vO3j4WzQ1yYQK 3leE068cydd8wshQ2uFqZO80GsPGIW1s60yOPYdppakSCMehyNQ3Z3gAYYPS77Tf0NedpeF3nvPK Qm0cI/+LuSKcKEghMbvDgA== --===============8429815862399578234==--