Bug #80458 Accessing partially intialized data: sql/sql_yacc.cc MYSQLparse() line ~18570
Submitted: 21 Feb 2016 23:01 Modified: 5 Sep 2019 12:24
Reporter: Sergey Sprogis Email Updates:
Status: Won't fix 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: mysqld, PIR

[21 Feb 2016 23:01] Sergey Sprogis
Description:
Oracle's tool called Discover detects PIR (Partially Initialized Read)
in mysqld server when executing '1st' test case located inside
MySQL 5.7.10. This PIR 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 reported by Discover looks like this:
======================================
(PIR): accessing partially initialized data in "yylsa + 888" ... (8 bytes) on the stack:
 MYSQLparse(THD*) + 0x36c4 <sql_yacc.cc : 18570>
  18570:=>  YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
 parse_sql(THD*, Parser_state*, Object_creation_ctx*) + 0x1454 <sql_parse.cc : 7005>
  7005:=>  bool mysql_parse_status= MYSQLparse(thd) != 0;
 parse_view_definition(THD*, TABLE_LIST*) + 0x2330 <sql_view.cc : 1365>
  1365:=> result= parse_sql(thd, &parser_state, view_ref->view_creation_ctx);
 open_table(THD*, TABLE_LIST*, Open_table_context*) + 0x4bac <sql_base.cc : 3372>
  3372:=> if (parse_view_definition(thd, table_list))
 open_and_process_table(THD*, LEX*, TABLE_LIST*, ... <sql_base.cc : 5052>
  5052:=>       error= open_table(thd, tables, ot_ctx);
 open_tables(THD*, TABLE_LIST**, unsigned int*, ... <sql_base.cc : 5663>
  5663:=> has_prelocking_list, &ot_ctx);
 open_tables_for_query(THD*, TABLE_LIST*, unsigned int) + 0x694 <sql_base.cc : 6422>
  6422:=> if (open_tables(thd, &tables, &thd->lex->table_count, flags,
 fill_schema_table_by_open(THD*, st_mem_root*,... <sql_show.cc : 3873>
  3873:=> MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0));
======================================

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 (yylsp-yylen) argument located
   inside sql_yacc.cc ,line 18570:

  YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);

  is reading 8 bytes from the address where only 4 bytes have been initialized.

   Below is 39 lines independent executable t.cc test case which accurately
   simulates original issue. For it Discover produces exactly the same  PIR
   message as for original mysqld server.

   Similarly to the original code this small test  reads 8 bytes from the address of yyn.
   Out of these 8 bytes,  4 bytes is initialized (integer variable yyn), hence it is
   reported as a partially initialized read.

t.cc
===================================
static const unsigned char yyr2[]={
 0,2,1,0,4,2,0,1,1,1,
 1,1,1,1,1,1,1,1,1,1,
 1,1,1,1,1,1,1,1,1,1,
 1,1,1,1,1,1,1,1,1,1,
 1,1,1,1,1,1,1,1,1,1,
 1,1,1,1,1,1,1,1,1,1,
 1,1,1,1,1,3
};
static const int yypact [ ] = {
  4769, 1920,  136,-4054,  -69,  904,55010,   56, 1324, 1324,
   510, 3368,-4054, 1179,-4054,-4054,-4054, 3324,-4054,55010,
   136,  401,-4054,27444,-4054, 1643,  768,  223,  198, 1324,
   136,  553,-4054,55010,-4054,  812, -143,  136, 1554,-4054,
 48610,-4054,  510,55010,-4054,30663,-4054,-4054,48610,  971,
   101,  707,  982,-4054, 1379,55010, 2129, -136, 1308, 1285,
 -4054,-4054, 1344,-4054,-4054,-4054
};
struct Symbol_location {
 const char *start;
 const char *end;
} ;
typedef struct YYLTYPE {
 Symbol_location cpp;
 Symbol_location raw;
} YYLTYPE ;
void MYSQLparse() {
 YYLTYPE yylsa[100],yyloc,*yylsp;
 int yylen;
 yylsp=yylsa;
 for (int yyn=65; yyn<=65; yyn++)  {
  yylen=yyr2[yyn];
  (yyloc).cpp.start=(((yylsp-yylen))[1]).cpp.start;
 }
}
int main() {
 MYSQLparse();
 return 0;
}
===================================

Suggested fix:
Somehow fully initialize (yylsp - yylen) value  before passing it into:
  YYLLOC_DEFAULT (yyloc, (yylsp - yylen), yylen);
[21 Feb 2016 23:39] Sergey Sprogis
Added MySQL version
[22 Feb 2016 12:57] MySQL Verification Team
Hello Sergey,

Thank you for the report!

Thanks,
Umesh
[5 Sep 2019 12:24] Erlend Dahl
Posted by developer:

The repro does NOT exhibit an UMR according to the normal definition used by
other memory checkers (e.g. Memory Sanitizer), because control flow does not
depend on the value of the uninitialized memory, it is merely a "dirty copy".
This is not uncommon there is little value in flagging it.
 
Anyway, in this particular case, there is not much we can do, as the code in
question is generated by Bison.