)]}'
{
  "commit": "0131aeb3c742cfdb109f63716123e8324b2a481e",
  "tree": "3e613d45f7a706cf5bfcf4ee0a254c8b83102614",
  "parents": [
    "26e4d0b9a3ef64c609f7ac331984aaea9f9b799f"
  ],
  "author": {
    "name": "Barret Rhoden",
    "email": "brho@cs.berkeley.edu",
    "time": "Mon Apr 08 09:19:16 2019 -0400"
  },
  "committer": {
    "name": "Barret Rhoden",
    "email": "brho@cs.berkeley.edu",
    "time": "Mon Apr 08 09:48:36 2019 -0400"
  },
  "message": "Fix refcounting problem in walk_symlink()\n\nThe old code would double-close \u0027symlink\u0027 if it ever got into the \"walk\nsucceeded but we were still on a symlink\" case.\n\nRecall that walk_symlink() either walks all the way, or not at all.  If\nthe old code (deleted) failed, we were supposed to close symlink.\nHowever, there was a case where walk_symlink would fail, but it would\nalso close the chan passed in: when the sub-walk succeeded, but\nwalk_symlink() still failed.  How could that fail?  With a symlink loop.\n\nIf that sounds confusing, it\u0027s because it is.  There are actually two\nspots of recursion, which might not even have been clear to me when I\nwrote this.  The main recursive path is walk -\u003e walk_symlink -\u003e walk -\u003e\nwalk_symlink.  We normally never got to the \"walk succeeded, then call\nwalk_symlink\" (deleted).  We usually only called walk_symlink() from\n*within* walk itself.\n\nYou could trigger this situation with a no_follow symlink\n(rename/remove, flags Aremove, flags no_follow).  Syzkaller basically\ndid this:\n\tln -s x x\n\trename x/x whatever\nSo we were walking the first x, had no_follow set, so walk -\u003e\nwalk_symlink for the first x.  Since there were more names in the\nmain/outer namec, we\u0027d get past the first no_follow.  Then that second\nwalk() would be for \"../x\", where no_follow would kick in, since there\nwere no longer any names left.  That walk_symlink() would return the\nsyml passed in, which appeared to walk to be a success (which it was).\n\nAnyway, this was calling the buggy post-successful-walk() part of\nwalk_symlink().  The first fix I had was to only cclose(symlink) when that\ninterior walk_symlink() succeeded.  Although that fixed the bug\n(walk_symlink either succeeds xor closes, so don\u0027t close on success),\nthe real problem was having that code at all.\n\nIf walk() lands on a symlink, walk itself should try to deal with it (by\ncalling walk_symlink()).  If walk returns (success or failure), we\nshould consider the walking of that symlink to be done, one way or\nanother.  If it is no_follow, we might be on a symlink (if last in the\npath).  If it was a mount_point, we could be on a symlink too.\n\nThat whole \"we\u0027re still on a symlink, let\u0027s walk it again\" was busted,\nand it would break when we had no_follow set globally and tried to\nfollow a legit intermediate symlink that had more than one link to\nfollow (but not 8 - the max).  And it was confusing.  Hopefully this\ncode makes a little more sense - esp when realizing that walk() sorts\nout symlinks by calling walk_symlink().  walk_symlink() shouldn\u0027t call\nitself, but it can call walk().  Up to 8 times.\n\nReported-by: syzbot+9eec51df84779065d6de@syzkaller.appspotmail.com\nSigned-off-by: Barret Rhoden \u003cbrho@cs.berkeley.edu\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "39298136838da7f0da5b0afe67da4fa8bd69ca07",
      "old_mode": 33188,
      "old_path": "kern/src/ns/chan.c",
      "new_id": "02f11fd8b5db8d21eb1c6944eb0f34817ce28e24",
      "new_mode": 33188,
      "new_path": "kern/src/ns/chan.c"
    }
  ]
}
