CVE-2020-28163: NULL Pointer Dereference (CWE-476)
value: libdwarf before 20201201 allows a dwarf_print_lines.c NULL pointer dereference and application crash via a DWARF5 line-table header that has an invalid FORM for a pathname.
CWE description: The product dereferences a pointer that it expects to be valid but is NULL.
CVSS v3 base score: 6.5
Published: 2023-04-16T00:15Z
Repository: https://github.com/davea42/libdwarf-code
Language: Other
Files changed: libdwarf/ChangeLog, libdwarf/dwarf_print_lines.c
Commit date: 2020-10-28 15:56:06 -0700
Commit message: modified:   libdwarf/dwarf_print_lines.c
    * dwarf_print_lines.c: In case of corrupted
      DWARF5 line header the fi_file_name field
      for a file entry can be null. Now
      we print a <no file name> string in that case
      to avoid passing a null to dwarfstring_append.
      Dwarfbug DW202010-003.
      Also some lines longer than libdwarf standard
      were shortened, but a few long lines really
      must remain.

Diff: diff --git a/libdwarf/ChangeLog b/libdwarf/ChangeLog
index d5fc623c..bbcfb33e 100644
--- a/libdwarf/ChangeLog
+++ b/libdwarf/ChangeLog
@@ -1,5 +1,15 @@
+2020-10-28: David Anderson
+    * dwarf_print_lines.c: In case of corrupted
+      DWARF5 line header the fi_file_name field
+      for a file entry can be null. Now
+      we print a <no file name> string in that case
+      to avoid passing a null to dwarfstring_append.
+      Dwarfbug DW202010-003.
+      Also some lines longer than libdwarf standard
+      were shortened, but a few long lines really
+      must remain.
 2020-10-28: David Anderson
     * dwarf_ranges.c: Altered lines longer than libdwarf
       standard.
 2020-10-27: David Anderson
     * dwarf_generic_init.c: Now checks for a null PATH
diff --git a/libdwarf/dwarf_print_lines.c b/libdwarf/dwarf_print_lines.c
index e119d666..8f73f7b9 100644
--- a/libdwarf/dwarf_print_lines.c
+++ b/libdwarf/dwarf_print_lines.c
@@ -52,12 +52,14 @@ static void
 print_line_header(Dwarf_Debug dbg,
     Dwarf_Bool is_single_tab,
     Dwarf_Bool is_actuals_tab)
 {
 if (!is_single_tab) {
-    /* Ugly indenting follows, it makes lines shorter to see them better.
-        Best to use a wider text window to really see how it looks.*/
+    /*  Ugly indenting follows, it makes lines shorter
+        to see them better.
+        Best to use a wider text window to really
+        see how it looks.*/
 if (is_actuals_tab) {
 _dwarf_printf(dbg,"\nActuals Table\n");
 _dwarf_printf(dbg,
 "                                                         be\n"
 "                                                         ls\n"
@@ -100,17 +102,19 @@ print_line_detail(
     dwarfstring_constructor_static(&m1,locallinebuf,
         sizeof(locallinebuf));
     if(!is_single_table && is_actuals_table) {
         dwarfstring_append_printf_s(&m1,"%-15s ",(char *)prefix);
         dwarfstring_append_printf_i(&m1,"%3d ",opcode);
-        dwarfstring_append_printf_u(&m1,"0x%" DW_PR_XZEROS DW_PR_DUx ,
+        dwarfstring_append_printf_u(&m1,"0x%" DW_PR_XZEROS DW_PR_DUx,
             regs->lr_address);
         dwarfstring_append_printf_u(&m1,"/%01u",regs->lr_op_index);
         dwarfstring_append_printf_u(&m1," %5lu", regs->lr_line);
         dwarfstring_append_printf_u(&m1," %3d",regs->lr_isa);
-        dwarfstring_append_printf_i(&m1,"   %1d", regs->lr_basic_block);
-        dwarfstring_append_printf_i(&m1,"%1d\n",  regs->lr_end_sequence);
+        dwarfstring_append_printf_i(&m1,"   %1d",
+            regs->lr_basic_block);
+        dwarfstring_append_printf_i(&m1,"%1d\n",
+            regs->lr_end_sequence);
         _dwarf_printf(dbg,dwarfstring_string(&m1));
         dwarfstring_destructor(&m1);
         return;
     }
     if(!is_single_table && !is_actuals_table) {
@@ -136,13 +140,15 @@ print_line_detail(
             regs->lr_subprogram) {
             dwarfstring_append_printf_u(&m1,
                 "   x%02" DW_PR_DUx ,
                 regs->lr_discriminator); /* DWARF4 */
             dwarfstring_append_printf_u(&m1,
-                "  x%02" DW_PR_DUx , regs->lr_call_context); /* EXPERIMENTAL */
+                "  x%02" DW_PR_DUx,
+                regs->lr_call_context); /* EXPERIMENTAL */
             dwarfstring_append_printf_u(&m1,
-                "  x%02" DW_PR_DUx , regs->lr_subprogram); /* EXPERIMENTAL */
+                "  x%02" DW_PR_DUx ,
+                regs->lr_subprogram); /* EXPERIMENTAL */
             dwarfstring_append_printf_i(&m1,
                 "  %1d", regs->lr_is_stmt);
             dwarfstring_append_printf_i(&m1,
                 "%1d", (int) regs->lr_isa);
             dwarfstring_append_printf_i(&m1,
@@ -234,11 +240,12 @@ print_include_directory_details(Dwarf_Debug dbg,
                 &tname);
             if ( res != DW_DLV_OK) {
                 tname = "<unknown type>";
             }
             dwarfstring_append_printf_u (&m4,
-                " type 0x%" DW_PR_XZEROS DW_PR_DUx ,valpair->up_first);
+                " type 0x%" DW_PR_XZEROS DW_PR_DUx,
+                valpair->up_first);
             dwarfstring_append_printf_s (&m4,
                 " %-20s\n",(char *)tname);
             res = dwarf_get_FORM_name(valpair->up_second,&fname);
             if ( res != DW_DLV_OK) {
                 fname = "<unknown form>";
@@ -325,24 +332,26 @@ print_just_file_entry_details(Dwarf_Debug dbg,
             but lets leave it there for the moment to avoid
             changing output.  */
         if (line_context->lc_file_entry_count > 9) {
             dwarfstring_append_printf_u(&m3,
                 "  file[%2u] ",fiu);
-            dwarfstring_append_printf_s(&m3,
-                "%-20s ",
-                (char *) fe->fi_file_name);
-            dwarfstring_append_printf_u(&m3,
-                "(file-number: %u)\n",
-                filenum);
         } else {
             dwarfstring_append_printf_u(&m3,
                 "  file[%u]  ", fiu);
-            dwarfstring_append_printf_s(&m3,
-                "%-20s ",(char *)fe->fi_file_name);
-            dwarfstring_append_printf_u(&m3,
-                "(file-number: %u)\n",filenum);
         }
+        /*  DWARF5 can have a null fi_file_name
+            if  the format code in the
+            line table header is unknown, such
+            as in a corrupt object file. */
+        dwarfstring_append_printf_s(&m3,
+            "%-20s ",
+            fe->fi_file_name?
+            (char *) fe->fi_file_name:
+            "<no file name>");
+        dwarfstring_append_printf_u(&m3,
+            "(file-number: %u)\n",
+            filenum);
         _dwarf_printf(dbg,dwarfstring_string(&m3));
         dwarfstring_reset(&m3);
         if (fe->fi_dir_index_present) {
             Dwarf_Unsigned di = 0;
             di = fe->fi_dir_index;
@@ -507,12 +516,12 @@ _dwarf_internal_printlines(Dwarf_Die die,
     /*  This pointer is used to scan the portion of the .debug_line
         section for the current cu. */
     Dwarf_Small *line_ptr = 0;
     Dwarf_Small *orig_line_ptr = 0;
 
-    /*  Pointer to a DW_AT_stmt_list attribute in case it exists in the
-        die. */
+    /*  Pointer to a DW_AT_stmt_list attribute in case
+        it exists in the die. */
     Dwarf_Attribute stmt_list_attr = 0;
 
     /*  Pointer to DW_AT_comp_dir attribute in die. */
     Dwarf_Attribute comp_dir_attr = 0;
 
@@ -566,11 +575,12 @@ _dwarf_internal_printlines(Dwarf_Die die,
     if (!dbg->de_debug_line.dss_size) {
         return (DW_DLV_NO_ENTRY);
     }
 
     address_size = _dwarf_get_address_size(dbg, die);
-    resattr = dwarf_attr(die, DW_AT_stmt_list, &stmt_list_attr, error);
+    resattr = dwarf_attr(die, DW_AT_stmt_list, &stmt_list_attr,
+        error);
     if (resattr != DW_DLV_OK) {
         return resattr;
     }
     /*  The list of relevant FORMs is small.
         DW_FORM_data4, DW_FORM_data8, DW_FORM_sec_offset
@@ -598,11 +608,12 @@ _dwarf_internal_printlines(Dwarf_Die die,
         return (DW_DLV_ERROR);
     }
     section_start =  dbg->de_debug_line.dss_data;
     {
         Dwarf_Unsigned fission_size = 0;
-        int resfis = _dwarf_get_fission_addition_die(die, DW_SECT_LINE,
+        int resfis = _dwarf_get_fission_addition_die(die,
+            DW_SECT_LINE,
             &fission_offset,&fission_size,error);
         if(resfis != DW_DLV_OK) {
             dwarf_dealloc(dbg,stmt_list_attr, DW_DLA_ATTR);
             return resfis;
         }
@@ -610,12 +621,12 @@ _dwarf_internal_printlines(Dwarf_Die die,
 
     orig_line_ptr = section_start + line_offset + fission_offset;
     line_ptr = orig_line_ptr;
     dwarf_dealloc(dbg, stmt_list_attr, DW_DLA_ATTR);
 
-    /*  If die has DW_AT_comp_dir attribute, get the string that names
-        the compilation directory. */
+    /*  If die has DW_AT_comp_dir attribute, get the string
+        that names the compilation directory. */
     resattr = dwarf_attr(die, DW_AT_comp_dir, &comp_dir_attr, error);
     if (resattr == DW_DLV_ERROR) {
         return resattr;
     }
     if (resattr == DW_DLV_OK) {
@@ -905,11 +916,12 @@ print_actuals_and_locals(Dwarf_Debug dbg,
             }
             if (line_context->lc_actuals_table_offset > 0) {
                 is_actuals_table = true;
                 /* Read Actuals */
 
-                print_line_header(dbg, is_single_table, is_actuals_table);
+                print_line_header(dbg, is_single_table,
+                    is_actuals_table);
                 res = read_line_table_program(dbg,
                     line_ptr_actuals, line_ptr_end, orig_line_ptr,
                     section_start,
                     line_context,
                     address_size, doaddrs, dolines,

Vulnerable code:     /* Ugly indenting follows, it makes lines shorter to see them better.
        Best to use a wider text window to really see how it looks.*/
        dwarfstring_append_printf_u(&m1,"0x%" DW_PR_XZEROS DW_PR_DUx ,
        dwarfstring_append_printf_i(&m1,"   %1d", regs->lr_basic_block);
        dwarfstring_append_printf_i(&m1,"%1d\n",  regs->lr_end_sequence);
                "  x%02" DW_PR_DUx , regs->lr_call_context); /* EXPERIMENTAL */
                "  x%02" DW_PR_DUx , regs->lr_subprogram); /* EXPERIMENTAL */
                " type 0x%" DW_PR_XZEROS DW_PR_DUx ,valpair->up_first);
            dwarfstring_append_printf_s(&m3,
                "%-20s ",
                (char *) fe->fi_file_name);
            dwarfstring_append_printf_u(&m3,
                "(file-number: %u)\n",
                filenum);
            dwarfstring_append_printf_s(&m3,
                "%-20s ",(char *)fe->fi_file_name);
            dwarfstring_append_printf_u(&m3,
                "(file-number: %u)\n",filenum);
    /*  Pointer to a DW_AT_stmt_list attribute in case it exists in the
        die. */
    resattr = dwarf_attr(die, DW_AT_stmt_list, &stmt_list_attr, error);
        int resfis = _dwarf_get_fission_addition_die(die, DW_SECT_LINE,
    /*  If die has DW_AT_comp_dir attribute, get the string that names
        the compilation directory. */
                print_line_header(dbg, is_single_table, is_actuals_table);
Fixed code: 2020-10-28: David Anderson
    * dwarf_print_lines.c: In case of corrupted
      DWARF5 line header the fi_file_name field
      for a file entry can be null. Now
      we print a <no file name> string in that case
      to avoid passing a null to dwarfstring_append.
      Dwarfbug DW202010-003.
      Also some lines longer than libdwarf standard
      were shortened, but a few long lines really
      must remain.
    /*  Ugly indenting follows, it makes lines shorter
        to see them better.
        Best to use a wider text window to really
        see how it looks.*/
        dwarfstring_append_printf_u(&m1,"0x%" DW_PR_XZEROS DW_PR_DUx,
        dwarfstring_append_printf_i(&m1,"   %1d",
            regs->lr_basic_block);
        dwarfstring_append_printf_i(&m1,"%1d\n",
            regs->lr_end_sequence);
                "  x%02" DW_PR_DUx,
                regs->lr_call_context); /* EXPERIMENTAL */
                "  x%02" DW_PR_DUx ,
                regs->lr_subprogram); /* EXPERIMENTAL */
                " type 0x%" DW_PR_XZEROS DW_PR_DUx,
                valpair->up_first);
        /*  DWARF5 can have a null fi_file_name
            if  the format code in the
            line table header is unknown, such
            as in a corrupt object file. */
        dwarfstring_append_printf_s(&m3,
            "%-20s ",
            fe->fi_file_name?
            (char *) fe->fi_file_name:
            "<no file name>");
        dwarfstring_append_printf_u(&m3,
            "(file-number: %u)\n",
            filenum);
    /*  Pointer to a DW_AT_stmt_list attribute in case
        it exists in the die. */
    resattr = dwarf_attr(die, DW_AT_stmt_list, &stmt_list_attr,
        error);
        int resfis = _dwarf_get_fission_addition_die(die,
            DW_SECT_LINE,
    /*  If die has DW_AT_comp_dir attribute, get the string
        that names the compilation directory. */
                print_line_header(dbg, is_single_table,
                    is_actuals_table);