Bug #80457 Accessing uninitialized memory inside mysys/my_malloc.c my_free() line ~129
Submitted: 21 Feb 2016 21:35 Modified: 25 Feb 2016 18:21
Reporter: Sergey Sprogis Email Updates:
Status: Closed Impact on me:
None 
Category:MySQL Server: Compiling Severity:S3 (Non-critical)
Version:5.7.10 OS:Solaris (Sparc, 11.3)
Assigned to: CPU Architecture:Any
Tags: my_free(), mysqld, UMR

[21 Feb 2016 21:35] Sergey Sprogis
Description:
Oracle's tool called Discover detects UMR (Uninitialized Memory Read)
in mysqld server when executing '1st' test case located inside
MySQL 5.7.10. This UMR was produced on Sparc Solaris 11.3 T7 system,
but it's not platform specific,and can occur on any other platform.

Error message and call stack reproted by Discover looks like this:
====================================== 
ERROR 1 (UMR): accessing uninitialized data in "*(mh + 16)" at address 0x11337be90 (8 bytes) on the heap:

  my_free() + 0x308 <my_malloc.c : 129>
   129:=> PSI_MEMORY_CALL(memory_free)(mh->m_key, mh->m_size, mh->m_owner);
  my_fclose() + 0x7dc <my_fopen.c : 207>
   207:=>  my_free(my_file_info[file].name);
  inline_mysql_file_fclose(const char*, ... <mysql_file.h : 870>
   870:=>  result= my_fclose(file->m_file, flags);
 search_default_file_with_ext(int (*)(void*,... <my_default.cc : 1110>
   1110:=>   mysql_file_fclose(fp, MYF(0));
  my_search_option_files() + 0x4244 <my_default.cc : 399>
   399:=> my_defaults_file, 0)) < 0)
  my_load_defaults() + 0xbcc <my_default.cc : 663>
   663:=>    if ((error= my_search_option_files(conf_file, argc, argv,
  load_defaults() + 0x1c8 <my_default.cc : 586>
   586:=>    return my_load_defaults(conf_file,...);
  mysqld_main(int, char**) + 0x404 <mysqld.cc : 4325>
   4325:=>   if (load_defaults(MYSQL_CONFIG_NAME,...))

  was allocated at (106 bytes):
   my_raw_malloc() + 0x2a4 <my_malloc.c : 191>
    191:=>      point= malloc(size);
   my_malloc() + 0x1a4 <my_malloc.c : 54>
    54:=>     mh= (my_memory_header*) my_raw_malloc(raw_size, flags);
   my_strdup() + 0x1b8 <my_malloc.c : 309>
    309:=>    if ((ptr= (char*) my_malloc(key, length, my_flags)))
   my_fopen() + 0x5f4 <my_fopen.c : 71>
    71:=>  dup_filename= my_strdup(key_memory_my_file_info, filename, MyFlags);
   inline_mysql_file_fopen(unsigned int, const char*,...<mysql_file.h : 835>
    835:=> that->m_file= my_fopen(filename, flags, myFlags);
   search_default_file_with_ext(int (*)(void*,... <my_default.cc : 908>
    908:=> if ( !(fp = mysql_file_fopen(key_file_cnf,...
   my_search_option_files() + 0x4244 <my_default.cc : 399>
    399:=> my_defaults_file, 0)) < 0)
   my_load_defaults() + 0xbcc <my_default.cc : 663>
    663:=> if ((error= my_search_option_files(conf_file, argc, argv,
==================================

How to repeat:
1. If you have access to Discover tool, and latest Sun Stidio C/C++ compilers
   you need to build MySQL-5.7.10 sources in debug mode on Solaris Sparc,
   then launch discover binary with input argument as mysqld binary,
   and when completed to take a look inside mysql.html file which should
   contain call stack shown in description above.

2. If you do not have access to Discover, you need to debug MySQL server
   with '1st' test case, and to see that mh->m_owner argument located
   inside my_malloc.c ,line 129:

   PSI_MEMORY_CALL(memory_free)(mh->m_key, mh->m_size, mh->m_owner);

   is indeed uninitialized before that point.
    
   Below is 49 lines independent executable t.c test case which accurately 
   simulates original issue. For it Discover produces exactly the same UMR 
   message as for original mysqld server.

 t.c
 ===================================
#include <stdlib.h>
struct PSI_thread;
typedef struct PSI_thread PSI_thread;
static unsigned memory_alloc_noop(size_t size,struct PSI_thread **owner) {
 return 0;
}
static void memory_free_noop(size_t size,struct PSI_thread *owner) {}
typedef unsigned (*memory_alloc_v1_t) (size_t size, struct PSI_thread **owner);
typedef void (*memory_free_v1_t) (size_t size, struct PSI_thread *owner);
struct PSI_v1 {
 memory_alloc_v1_t memory_alloc;
 memory_free_v1_t memory_free;
};
typedef struct PSI_v1 PSI;
static PSI PSI_noop={memory_alloc_noop,memory_free_noop};
PSI *PSI_server= & PSI_noop;
struct my_memory_header {
 size_t m_size;
 PSI_thread *m_owner;
};
typedef struct my_memory_header my_memory_header;
static void *my_raw_malloc(size_t size) {
 void *point;
 point=malloc(size);
 return point;
}
void *my_malloc(unsigned size) {
 my_memory_header *mh;
 size_t raw_size;
 raw_size=32+size;
 mh=(my_memory_header*)my_raw_malloc(raw_size);
 void *user_ptr;
 mh->m_size=size;
 PSI_server->memory_alloc (size, & mh->m_owner);
 user_ptr=((( char*) mh)+32);
 return user_ptr;
}
void my_free(void *ptr) {
 my_memory_header *mh;
 mh=((my_memory_header*)(((char*)ptr)-32));
 PSI_server->memory_free(mh->m_size, mh->m_owner);
 free(mh);
}
int main() {
 void *p;
 p=my_malloc(16);
 my_free(p);
 return 0;
}
=====================================
 

Suggested fix:
Somehow initialize mh->m_owner before using passing its value into PSI_server->memory_free()
[22 Feb 2016 12:57] MySQL Verification Team
Hello Sergey,

Thank you for the report!

Thanks,
Umesh
[25 Feb 2016 18:21] Paul DuBois
Noted in 5.7.12, 5.8.0 changelogs.

A Valgrind warning for memory_free_noop() was silenced.