Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rNlIf-00FPSn-Ki for pgsql-bugs@arkaria.postgresql.org; Thu, 11 Jan 2024 03:00:10 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1rNlIe-008l1T-08 for pgsql-bugs@arkaria.postgresql.org; Thu, 11 Jan 2024 03:00:08 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1rNlId-008l1K-Gd for pgsql-bugs@lists.postgresql.org; Thu, 11 Jan 2024 03:00:07 +0000 Received: from mail-lj1-x232.google.com ([2a00:1450:4864:20::232]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1rNlIa-000wNB-JM for pgsql-bugs@lists.postgresql.org; Thu, 11 Jan 2024 03:00:06 +0000 Received: by mail-lj1-x232.google.com with SMTP id 38308e7fff4ca-2cd46e7ae8fso53373011fa.1 for ; Wed, 10 Jan 2024 19:00:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1704942003; x=1705546803; darn=lists.postgresql.org; h=content-transfer-encoding:in-reply-to:from:references:cc:to :content-language:subject:user-agent:mime-version:date:message-id :from:to:cc:subject:date:message-id:reply-to; bh=vVexd8xBw6sRCTaiF2j1hgsBibH0bbDgzkC4A1dyZ+8=; b=ektbqZNa0ula9XhyJE3zyV/J4rShpQ8CHubH609om6g4j2qRu6GVulWC+NCFFAydu4 jLaahrrRtjjU0K9ynMguwBdRPAeJseuuJyMGyWfiiq+PvIvXfgeRTwMKd7MtbFTtWXe2 b+8ZvsKm95l8GaEKZN54oAdOUOsUFvdnnlCeymr9a8EJMBBsjudjBtFYoV6fY6khDyo5 lpgWxOCiLEMz8DD2djxPLxxiQJ70gRj3cGLLaeoXhEdjGDWM0n1aiKXAft/yIFAuRUOD eIJ+z891KdOt6xRsudhMuw3lTUEQW2LtVYtduOJ20e5L/uuJ+GxJFH81OboC7SrF9Vl8 WYwQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704942003; x=1705546803; h=content-transfer-encoding:in-reply-to:from:references:cc:to :content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=vVexd8xBw6sRCTaiF2j1hgsBibH0bbDgzkC4A1dyZ+8=; b=M0yi/i/KDNvjgle39bYmx3MiyHm8wI0J17Oj4GlngMtTUZGMo/uBj2O82LaxUnz+s2 CB1vHvOMN0Pofnpuoe18iTtQnPdQNqe2sCvi3cBTL1NnUyle0tsiNWNk9VYLV1LGu/Ty p6quEzND7auk7CtyA4ngEF/lY/X6Rw1E3ATnoCQJ7jQnvHI22IJJIhrI1fl0eIwPgEfn 4N1WGGncQyFtIpwtTRaV+YIQSGTqes4EtPcm/+ukD1byaHP1Drbgwm2SXTv1h6vjWxUp kYPZLeCE8A48koNjgXNOQwWOW+48qtDv/V/dJfNXUk/rQXR7rjczCxdJ9+XPqWSkS6ld HOig== X-Gm-Message-State: AOJu0Yz6ilvGKvmxtTCSGTIKwUjwUPPMgJr2B7G1yI6cH2+DShi4yHpV W8QqV1kQSofk+m3itXTsLIo= X-Google-Smtp-Source: AGHT+IFnAbO2/42Q6h6+bOqHZXkMqfNYnbiDUo+gL0xBBWm80ZZrm3DQ+Ot1y30VsEaIPsLhg7BVPQ== X-Received: by 2002:a2e:3504:0:b0:2cd:6eb4:1dff with SMTP id z4-20020a2e3504000000b002cd6eb41dffmr108132ljz.114.1704942002540; Wed, 10 Jan 2024 19:00:02 -0800 (PST) Received: from [1.0.0.7] ([91.185.77.50]) by smtp.gmail.com with ESMTPSA id h19-20020a2e9ed3000000b002cd35d8b018sm11840ljk.113.2024.01.10.19.00.01 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 10 Jan 2024 19:00:01 -0800 (PST) Message-ID: Date: Thu, 11 Jan 2024 06:00:00 +0300 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.4.2 Subject: Re: BUG #17798: Incorrect memory access occurs when using BEFORE ROW UPDATE trigger Content-Language: en-US To: Robert Haas Cc: Tom Lane , pgsql-bugs@lists.postgresql.org, Richard Guo , Andres Freund , Peter Geoghegan References: <17798-0907404928dcf0dd@postgresql.org> <87fe6c28-80b2-f709-a8db-d105e5240e39@gmail.com> <1849610.1682807087@sss.pgh.pa.us> <494cd7c4-c109-6886-c0e3-138a95e7e38b@gmail.com> <74335d44-819d-60e0-859a-efe74d9c84c2@gmail.com> From: Alexander Lakhin In-Reply-To: Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk 10.01.2024 23:04, Robert Haas wrote: > OK, thanks for the further explanation. I think I'm starting to > understand what's going on here. Maybe you already understand all of > this, but in my own words: > > ExecGetUpdateNewTuple() calls ExecProject() which documents that the > results are short-lived and you should materialize the slot if that's > a problem. ExecModifyTable() does slot = ExecGetUpdateNewTuple(...), > does not materialize it, and then passes that slot as the penultimate > argument to ExecUpdate(), which calls ExecUpdatePrologue(), which > calls ExecBRUpdateTriggers(), and the value returned by > ExecModifyTable()'s call to ExecGetUpdateNewTuple() is now stored in > newslot, but it still hasn't been materialized. So any operation > that's performed on oldslot at this point invalidates newslot. Yes, I see this the same way. > But ExecBRUpdateTriggers() now calls GetTupleForTrigger() to clobber > oldslot, so now newslot is also junk. That's technically OK, because > we're not going to access the contents of newslot any more. We're > instead going to refresh them: > > if (epqslot_candidate != NULL) > { > TupleTableSlot *epqslot_clean; > > epqslot_clean = ExecGetUpdateNewTuple(relinfo, epqslot_candidate, > oldslot); > > if (newslot != epqslot_clean) > ExecCopySlot(newslot, epqslot_clean); > } > > Since the slot returned by this call to ExecGetUpdateNewTuple() is a > function of the same projection info as when it was called from > ExecModifyTable(), the slot returned here must be the same slot that > was returned there, namely newslot. So, as you say, newslot == > epqslot_clean, and we've just clobbered it with updated values. > Therefore we're back in the situation where oldslot is fully valid and > newslot is only valid until somebody does something to oldslot. Thus, > the portion of your patch that simplifies this code doesn't fix any > bug, but rather just simplifies some unnecessarily complex logic. Yes, in that portion I was just trying to address Tom's suspicion: "Maybe we need to materialize even if newslot != epqslot_clean"... > But > the next thing that happens in the existing code is: > > trigtuple = ExecFetchSlotHeapTuple(oldslot, true, &should_free_trig); > > That is going to again invalidate newslot, because it can materialize > oldslot. That's a problem if we ever use newslot after this point, and > we do, unless we materialize newslot first, so your patch adds > ExecMaterializeSlot(newslot) right before this, fixing the problem. Yes, that's the way I find possible to fix it. > What I don't really understand is why failure to do this causes an > invalid memory access in ExecFetchSlotHeapTuple(). According to the > above analysis, the invalid memory access ought to occur the first > time something touches newslot after this call to > ExecFetchSlotHeapTuple(), but if I understand your valgrind trace > correctly, it's showing that ExecFetchSlotHeapTuple() is touching > something it shouldn't. Do you understand what's happening here? Maybe you are confused by the first call to ExecFetchSlotHeapTuple()? The full Valgrind stack from my yesterday's test run is: ==00:00:00:05.052 3934530== Invalid read of size 1 ==00:00:00:05.052 3934530==    at 0x1EECAC: heap_compute_data_size (heaptuple.c:244) ==00:00:00:05.052 3934530==    by 0x1EFA84: heap_form_tuple (heaptuple.c:1158) ==00:00:00:05.052 3934530==    by 0x423B5E: tts_buffer_heap_materialize (execTuples.c:747) ==00:00:00:05.052 3934530==    by 0x424EB1: ExecFetchSlotHeapTuple (execTuples.c:1653) ==00:00:00:05.052 3934530==    by 0x3ECD45: ExecBRUpdateTriggers (trigger.c:3055) ==00:00:00:05.052 3934530==    by 0x4483F0: ExecUpdatePrologue (nodeModifyTable.c:1923) ==00:00:00:05.052 3934530==    by 0x44A146: ExecUpdate (nodeModifyTable.c:2284) ==00:00:00:05.052 3934530==    by 0x44CE4E: ExecModifyTable (nodeModifyTable.c:3844) ==00:00:00:05.052 3934530==    by 0x41E9A1: ExecProcNodeFirst (execProcnode.c:464) ==00:00:00:05.052 3934530==    by 0x416F5D: ExecProcNode (executor.h:273) ==00:00:00:05.052 3934530==    by 0x416F5D: ExecutePlan (execMain.c:1670) ==00:00:00:05.052 3934530==    by 0x417120: standard_ExecutorRun (execMain.c:365) ==00:00:00:05.052 3934530==    by 0x4171FA: ExecutorRun (execMain.c:309) ==00:00:00:05.052 3934530==  Address 0x9982c74 is in a rw- anonymous segment ==00:00:00:05.052 3934530== here trigger.c:3055 points at the second call in ExecBRUpdateTriggers():             newtuple = ExecFetchSlotHeapTuple(newslot, true, &should_free_new); Does it makes more sense? > I think the question we should be asking ourselves here is whether > we're really OK with having such a vast distance between the point > where newslot is first populated and where materialization happens. It > seems like playing with fire to populate a slot with ephemeral data in > ExecModifyTable() and then press the materialize button four stack > frames down. There's a lot of opportunity for things to go wrong > there. On the other hand, this is very performance-sensitive code, so > maybe materializing here is the only workable solution. If that's the > case, though, we should probably add comments making the lifetime of > various slots clearer, because neither Tom nor I was able to > understand what the heck was happening here on first reading, which is > probably not a good sign. I was discouraged by that vast distance and implicit buffer usage too, but I found no other feasible way to fix it. On the other hand, 75e03eabe and Andres's words upthread made me believe that it's an acceptable solution. Thank you very much for diving deep into this subject! Best regards, Alexander