Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Fix minor nbtree page deletion buffer lock issue.
authorPeter Geoghegan <pg@bowt.ie>
Sat, 25 Apr 2020 21:17:02 +0000 (14:17 -0700)
committerPeter Geoghegan <pg@bowt.ie>
Sat, 25 Apr 2020 21:17:02 +0000 (14:17 -0700)
Avoid accessing the deletion target page's special area during nbtree
page deletion at a point where there is no buffer lock held.  This issue
was detected by a patch that extends Valgrind's memcheck tool to mark
nbtree pages that are unsafe to access (due to not having a buffer lock
or buffer pin) as NOACCESS.

We do hold a buffer pin at this point, and only access the special area,
so the old approach was safe.  Even still, it seems like a good idea to
tighten up the rules in this area.  There is no reason to not simply
insist on always holding a buffer lock (not just a pin) when accessing
nbtree pages.

Update some related comments in passing.

Discussion: https://postgr.es/m/CAH2-WzkLgyN3zBvRZ1pkNJThC=xi_0gpWRUb_45eexLH1+k2_Q@mail.gmail.com

src/backend/access/nbtree/nbtpage.c

index 39b8f17f4b5c2d9898c0bc13ce6e2dadf9197c39..321d887ff77eaaad0359e2c057f5517891d2e586 100644 (file)
@@ -1611,7 +1611,7 @@ _bt_pagedel(Relation rel, Buffer buf)
                 * to-be-deleted doesn't have a downlink, and the page
                 * deletion algorithm isn't prepared to handle that.
                 */
-               if (!P_LEFTMOST(opaque))
+               if (leftsib != P_NONE)
                {
                    BTPageOpaque lopaque;
                    Page        lpage;
@@ -1695,6 +1695,12 @@ _bt_pagedel(Relation rel, Buffer buf)
         * the only child of the parent, and could be removed. It would be
         * picked up by the next vacuum anyway, but might as well try to
         * remove it now, so loop back to process the right sibling.
+        *
+        * Note: This relies on the assumption that _bt_getstackbuf() will be
+        * able to reuse our original descent stack with a different child
+        * block (provided that the child block is to the right of the
+        * original leaf page reached by _bt_search()). It will even update
+        * the descent stack each time we loop around, avoiding repeated work.
         */
        if (!rightsib_empty)
            break;
@@ -1904,8 +1910,8 @@ _bt_mark_page_halfdead(Relation rel, Buffer leafbuf, BTStack stack)
  * leaf page is deleted.
  *
  * Returns 'false' if the page could not be unlinked (shouldn't happen).
- * If the (new) right sibling of the page is empty, *rightsib_empty is set
- * to true.
+ * If the (current) right sibling of the page is empty, *rightsib_empty is
+ * set to true.
  *
  * Must hold pin and lock on leafbuf at entry (read or write doesn't matter).
  * On success exit, we'll be holding pin and write lock.  On failure exit,