)]}'
{
  "commit": "3d2d21728b6db4430ff168ee27e12fc6e2627fad",
  "tree": "03d5906e31fbb7d539c1b7d7cd7c44fa2ee4ecb8",
  "parents": [
    "dd88f42597e903a7847b9ffc10fee4fbbbd66cf8"
  ],
  "author": {
    "name": "Pedro Alves",
    "email": "pedro@palves.net",
    "time": "Wed Feb 21 16:23:55 2024 +0000"
  },
  "committer": {
    "name": "Pedro Alves",
    "email": "pedro@palves.net",
    "time": "Mon Feb 26 15:09:38 2024 +0000"
  },
  "message": "[gdb] Fix heap-use-after-free in select_event_lwp\n\nPR gdb/31259 reveals one scenario where we run into a\nheap-use-after-free reported by thread sanitizer, while running\ngdb.base/vfork-follow-parent.exp.\n\nThe heap-use-after-free happens during the following scenario:\n\n - linux_nat_wait_1 is about to return an event for T2.  It stops all\n   other threads, and while doing so, stop_wait_callback -\u003e wait_lwp\n   sees T1 exit, and decides to leave the exit event pending.  It\n   should have set the lp-\u003estopped flag too, but does not -- this is\n   the bug.\n\n - The event for T2 is reported, is processed by infrun, and we\u0027re\n   back at linux_nat_wait_1.\n\n - linux_nat_wait_1 selects LWP T1 with the pending exit status to\n   report.\n\n - it sets variable lp to point to the corresponding lwp_info.\n\n - it calls stop_callback and stop_wait_callback for all threads\n   (because !target_is_non_stop_p ()).\n\n - it calls select_event_lwp to maybe pick another thread than T1, to\n   prevent starvation.\n\nThe problem is the following:\n\n - while calling stop_wait_callback for all threads, it also does this\n   for T1.  While doing so, the corresponding lwp_info is deleted\n   (callstack stop_wait_callback -\u003e wait_lwp -\u003e exit_lwp -\u003e\n   delete_lwp), leaving variable lp as a dangling pointer.\n\n - variable lp is passed to select_event_lwp, which derefences it,\n   which causes the heap-use-after-free.\n\nNote that the comment here mentions \"all other LWP\u0027s\":\n...\n      /* Now stop all other LWP\u0027s ...  */\n      iterate_over_lwps (minus_one_ptid, stop_callback);\n      /* ... and wait until all of them have reported back that\n        they\u0027re no longer running.  */\n      iterate_over_lwps (minus_one_ptid, stop_wait_callback);\n...\n\nThe reason the comments say \"all other LWP\u0027s\", and doesn\u0027t bother\nfiltering out LP is that lp-\u003estopped should be true at this point, and\nthe callbacks (both stop_callback and stop_wait_callback) check that\nflag, and do nothing if set.  I.e., they skip already-stopped threads,\nso they should skip LP.\n\nIn this particular scenario, though, we missed setting the stopped\nflag right in the first step described above, so LP was iterated over\nincorrectly.\n\nThe fix is to make wait_lwp set the lp-\u003estopped flag when it decides\nto leave the exit event pending.  However, going a bit further,\ngdbserver has a mark_lwp_dead function to centralize setting up\nvarious lwp flags such that the rest of the code doesn\u0027t mishandle\nthem, and it seems like a good idea to do a similar thing in gdb as\nwell.  That is what this patch does.\n\nPR gdb/31259\nBug: https://sourceware.org/bugzilla/show_bug.cgi?id\u003d31259\nCo-Authored-By: Tom de Vries \u003ctdevries@suse.de\u003e\nChange-Id: I4a6169976f89bf714c478cbb2b7d4c32365e62a9\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "71669863c5b9899fed02c8ae8cc81e719c691986",
      "old_mode": 33188,
      "old_path": "gdb/linux-nat.c",
      "new_id": "3ba072bc8d702b4998007e691ab653cef3683e7c",
      "new_mode": 33188,
      "new_path": "gdb/linux-nat.c"
    }
  ]
}
