Hi Chao,

On Fri, Oct 12, 2018 at 10:00:20PM +0800, Chao Yu wrote:
> Hi Dan,
> 
> Firstly, thanks for the report. :)
> 
> On 2018-10-11 20:23, Dan Carpenter wrote:
> > Hello Chao Yu,
> > 
> > The patch df634f444ee9: "f2fs: use rb_*_cached friends" from Oct 4,
> > 2018, leads to the following static checker warning:
> > 
> >     fs/f2fs/extent_cache.c:606 f2fs_update_extent_tree_range()
> >     error: uninitialized symbol 'leftmost'.
> > 
> > fs/f2fs/extent_cache.c
> >    497  static void f2fs_update_extent_tree_range(struct inode *inode,
> >    498                                  pgoff_t fofs, block_t blkaddr, 
> > unsigned int len)
> >    499  {
> >    500          struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
> >    501          struct extent_tree *et = F2FS_I(inode)->extent_tree;
> >    502          struct extent_node *en = NULL, *en1 = NULL;
> >    503          struct extent_node *prev_en = NULL, *next_en = NULL;
> >    504          struct extent_info ei, dei, prev;
> >    505          struct rb_node **insert_p = NULL, *insert_parent = NULL;
> >    506          unsigned int end = fofs + len;
> >    507          unsigned int pos = (unsigned int)fofs;
> >    508          bool updated = false;
> >    509          bool leftmost;
> >    510  
> >    511          if (!et)
> >    512                  return;
> >    513  
> >    514          trace_f2fs_update_extent_tree_range(inode, fofs, blkaddr, 
> > len);
> >    515  
> >    516          write_lock(&et->lock);
> >    517  
> >    518          if (is_inode_flag_set(inode, FI_NO_EXTENT)) {
> >    519                  write_unlock(&et->lock);
> >    520                  return;
> >    521          }
> >    522  
> >    523          prev = et->largest;
> >    524          dei.len = 0;
> >    525  
> >    526          /*
> >    527           * drop largest extent before lookup, in case it's already
> >    528           * been shrunk from extent tree
> >    529           */
> >    530          __drop_largest_extent(et, fofs, len);
> >    531  
> >    532          /* 1. lookup first extent node in range [fofs, fofs + len - 
> > 1] */
> >    533          en = (struct extent_node 
> > *)f2fs_lookup_rb_tree_ret(&et->root,
> >    534                                          (struct rb_entry 
> > *)et->cached_en, fofs,
> >    535                                          (struct rb_entry 
> > **)&prev_en,
> >    536                                          (struct rb_entry 
> > **)&next_en,
> >    537                                          &insert_p, &insert_parent, 
> > false,
> >    538                                          &leftmost);
> >                                                  ^^^^^^^^
> > Not always initialized in there.
> 
> I think the behavior should be acting as designed.
> 
> f2fs_lookup_rb_tree_ret()
> {
> ...
>       *insert_p = NULL;
>       *insert_parent = NULL;
>       *prev_entry = NULL;
>       *next_entry = NULL;
> 
>       if (RB_EMPTY_ROOT(&root->rb_root))
>               return NULL;
> 
>       if (re) {
>               if (re->ofs <= ofs && re->ofs + re->len > ofs)
>                       goto lookup_neighbors;
>       }
> 
> Until here, @leftmost has not been initialized, but both *insert_p and
> *insert_parent are NULL,
> 
>       if (leftmost)
>               *leftmost = true;
> ...
> }
> 
> Later in __insert_extent_tree()
> 
> we will not hit below condition because insert_p & insert_parent are not 
> valid:
>       if (insert_p && insert_parent) {
>               parent = insert_parent;
>               p = insert_p;
>               goto do_insert;
>       }
> 
>       leftmost = true;
> 
> So finally, we will initialize leftmost's value here, anyway, I think there is
> such place we use an uninitialized variable.
> 
> BTW, do we have any chance to detect such case in smatch?
> 
> Thanks,
> 
> > 
> >    539          if (!en)
> >    540                  en = next_en;
> >    541  
> >    542          /* 2. invlidate all extent nodes in range [fofs, fofs + len 
> > - 1] */
> >    543          while (en && en->ei.fofs < end) {
> >    544                  unsigned int org_end;
> >    545                  int parts = 0;  /* # of parts current extent split 
> > into */
> >    546  
> >    547                  next_en = en1 = NULL;
> >    548  
> >    549                  dei = en->ei;
> >    550                  org_end = dei.fofs + dei.len;
> >    551                  f2fs_bug_on(sbi, pos >= org_end);
> >    552  
> >    553                  if (pos > dei.fofs &&   pos - dei.fofs >= 
> > F2FS_MIN_EXTENT_LEN) {
> >    554                          en->ei.len = pos - en->ei.fofs;
> >    555                          prev_en = en;
> >    556                          parts = 1;
> >    557                  }
> >    558  
> >    559                  if (end < org_end && org_end - end >= 
> > F2FS_MIN_EXTENT_LEN) {
> >    560                          if (parts) {
> >    561                                  set_extent_info(&ei, end,
> >    562                                                  end - dei.fofs + 
> > dei.blk,
> >    563                                                  org_end - end);
> >    564                                  en1 = __insert_extent_tree(sbi, et, 
> > &ei,
> >    565                                                          NULL, NULL, 
> > true);
> >    566                                  next_en = en1;
> >    567                          } else {
> >    568                                  en->ei.fofs = end;
> >    569                                  en->ei.blk += end - dei.fofs;
> >    570                                  en->ei.len -= end - dei.fofs;
> >    571                                  next_en = en;
> >    572                          }
> >    573                          parts++;
> >    574                  }
> >    575  
> >    576                  if (!next_en) {
> >    577                          struct rb_node *node = 
> > rb_next(&en->rb_node);
> >    578  
> >    579                          next_en = rb_entry_safe(node, struct 
> > extent_node,
> >    580                                                  rb_node);
> >    581                  }
> >    582  
> >    583                  if (parts)
> >    584                          __try_update_largest_extent(et, en);
> >    585                  else
> >    586                          __release_extent_node(sbi, et, en);
> >    587  
> >    588                  /*
> >    589                   * if original extent is split into zero or two 
> > parts, extent
> >    590                   * tree has been altered by deletion or insertion, 
> > therefore
> >    591                   * invalidate pointers regard to tree.
> >    592                   */
> >    593                  if (parts != 1) {
> >    594                          insert_p = NULL;
> >    595                          insert_parent = NULL;
> >    596                  }
> >    597                  en = next_en;
> >    598          }
> >    599  
> >    600          /* 3. update extent in extent cache */
> >    601          if (blkaddr) {
> >    602  
> >    603                  set_extent_info(&ei, fofs, blkaddr, len);
> >    604                  if (!__try_merge_extent_node(sbi, et, &ei, prev_en, 
> > next_en))
> >    605                          __insert_extent_tree(sbi, et, &ei,
> >    606                                          insert_p, insert_parent, 
> > leftmost);
> >                                                                          
> > ^^^^^^^^
> > Smatch complains, but I'm to stupid to know if it's valid.
> > 
> >    607  
> >    608                  /* give up extent_cache, if split and small updates 
> > happen */
> >    609                  if (dei.len >= 1 &&
> >    610                                  prev.len < F2FS_MIN_EXTENT_LEN &&
> >    611                                  et->largest.len < 
> > F2FS_MIN_EXTENT_LEN) {
> >    612                          et->largest.len = 0;
> >    613                          et->largest_updated = true;
> >    614                          set_inode_flag(inode, FI_NO_EXTENT);
> >    615                  }
> >    616          }
> > 
> > regards,
> > dan carpenter
> > 

I get an UBSAN warning on this same line, so this seems to be a real bug.
It goes away if I initialize 'leftmost'.

To reproduce, build a kernel with CONFIG_F2FS_FS=y CONFIG_F2FS_FS_ENCRYPTION=y
CONFIG_UBSAN=y CONFIG_KASAN=y and run 'kvm-xfstests -c f2fs generic/397'.
(There's probably a better reproducer, but that's how I saw it.)

This is on latest mainline.

================================================================================
UBSAN: Undefined behaviour in fs/f2fs/extent_cache.c:605:4
load of value 255 is not a valid value for type '_Bool'
CPU: 0 PID: 94 Comm: kworker/u4:2 Not tainted 5.0.0-rc1-00043-g1bdbe22749207 #14
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1 04/01/2014
Workqueue: writeback wb_workfn (flush-254:32)
Call Trace:
 __dump_stack lib/dump_stack.c:77 [inline]
 dump_stack+0x70/0xa5 lib/dump_stack.c:113
 ubsan_epilogue+0xd/0x40 lib/ubsan.c:159
 __ubsan_handle_load_invalid_value+0x8e/0xa0 lib/ubsan.c:457
 f2fs_update_extent_tree_range+0x6e9/0x760 fs/f2fs/extent_cache.c:605
 f2fs_update_extent_cache+0xce/0xf0 fs/f2fs/extent_cache.c:804
 f2fs_update_data_blkaddr+0x18/0x20 fs/f2fs/data.c:639
 f2fs_outplace_write_data+0x63/0x130 fs/f2fs/segment.c:3147
 f2fs_do_write_data_page+0x3ae/0x7e0 fs/f2fs/data.c:1892
 __write_data_page+0x734/0xd00 fs/f2fs/data.c:1993
 f2fs_write_cache_pages+0x1fb/0x610 fs/f2fs/data.c:2160
 __f2fs_write_data_pages fs/f2fs/data.c:2273 [inline]
 f2fs_write_data_pages+0x3dc/0x450 fs/f2fs/data.c:2300
 do_writepages+0x43/0xf0 mm/page-writeback.c:2335
 __writeback_single_inode+0x56/0x660 fs/fs-writeback.c:1316
 writeback_sb_inodes+0x20b/0x6b0 fs/fs-writeback.c:1580
 wb_writeback+0x10f/0x510 fs/fs-writeback.c:1756
 wb_do_writeback fs/fs-writeback.c:1901 [inline]
 wb_workfn+0xa7/0x670 fs/fs-writeback.c:1942
 process_one_work+0x25d/0x670 kernel/workqueue.c:2153
 worker_thread+0x3e/0x3d0 kernel/workqueue.c:2296
 kthread+0x124/0x140 kernel/kthread.c:246
 ret_from_fork+0x24/0x30 arch/x86/entry/entry_64.S:352
================================================================================


_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

Reply via email to