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 1tBFL2-002hK5-UR for pgsql-bugs@arkaria.postgresql.org; Wed, 13 Nov 2024 15:31:24 +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 1tBFKz-00F78x-No for pgsql-bugs@arkaria.postgresql.org; Wed, 13 Nov 2024 15:31:22 +0000 Received: from magus.postgresql.org ([2a02:c0:301:0:ffff::29]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tBFCH-00F2i6-Vz; Wed, 13 Nov 2024 15:22:22 +0000 Received: from sender4-op-o11.zoho.com ([136.143.188.11]) by magus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1tBFCD-001jk7-Ki; Wed, 13 Nov 2024 15:22:21 +0000 ARC-Seal: i=1; a=rsa-sha256; t=1731511334; cv=none; d=zohomail.com; s=zohoarc; b=EGPviRSbMFuuGosVFg77PlF5XqAl3dhneoCN5Sl0d9W9vyDDgwsaSJNVaeFjbuPIKUntMUbj9u1kF8t3Vn4C7WQoHPLKwsvk+Ng6gKOGWa5TghTy+UcbxP+jfusT5mPfSsqN2ZgDkJGbIsMtuQ2SGhUqha9HvrkKIMuP9lzKbsw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1731511334; h=Content-Type:Cc:Cc:Date:Date:From:From:MIME-Version:Message-ID:Subject:Subject:To:To:Message-Id:Reply-To; bh=drA8lbYC/r1dAdYMC+b+KJ7Hmn79yxsf8+B4HHUdp4U=; b=abkTtcUfSBNeIsQfEEeieDaaz6gL45SmRV/lhB6eygc04yfF+Wpq1WOTW5wcWmtpEedlD1zYyeQ0om0sswtOHL+kBUjdnW0cXqNzraIc2mrYERCwnt3Zviv/aZ2jICOeQ8IxiLZ3yz4OzQboJFf7UsfzvA9iorWxIGn5gWOVRRY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass header.i=zohocorp.com; spf=pass smtp.mailfrom=jayesh.dp@zohocorp.com; dmarc=pass header.from= DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1731511334; s=admin; d=zohocorp.com; i=jayesh.dp@zohocorp.com; h=Date:Date:From:From:To:To:Cc:Cc:Message-Id:Message-Id:In-Reply-To:Subject:Subject:MIME-Version:Content-Type:Reply-To; bh=drA8lbYC/r1dAdYMC+b+KJ7Hmn79yxsf8+B4HHUdp4U=; b=rePDc9U5hEZU4MGsMf+KtfBIiJD3mwijZVyCtFaQq3MR7sSUM6CCxrFeZ2nUxMQl pAux05XwMvvB1UkW6S28M0hzdIIFQa39j3Q1RW1ItpLn9Ok5I8KJPS6rXRRirtGvJmB 9LIqSX/Te+nKPwZgFd+2knz4/dPBFzXl9sMWq3lo= Received: from mail.zoho.com by mx.zohomail.com with SMTP id 1731511331977512.5004731086875; Wed, 13 Nov 2024 07:22:11 -0800 (PST) Date: Wed, 13 Nov 2024 20:52:11 +0530 From: Jayesh Dehankar To: "pgsql-hackers" Cc: "zlabs-cstore@zohocorp.com" , "pgsql-hackers" , "pgsql-bugs" Message-Id: <193261e2c4d.3dd3cd7c1842.871636075166132237@zohocorp.com> In-Reply-To: Subject: [Bug] Heap Use After Free in Window Aggregate Execution MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_Part_6447_1211805977.1731511331918" Importance: Medium User-Agent: Zoho Mail X-Mailer: Zoho Mail X-Zoho-Virus-Status: 1 X-Zoho-AV-Stamp: zmail-av-1.3.1/231.467.98 List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk ------=_Part_6447_1211805977.1731511331918 Content-Type: multipart/alternative; boundary="----=_Part_6448_1577446894.1731511331918" ------=_Part_6448_1577446894.1731511331918 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Hi Developers, We have discovered a bug in PostgreSQL v16.3 related to a top-level window = aggregate with a partition-by clause. The issue occurs when the run conditi= on fails, causing the window aggregate status to change from WINDOWAGG_RUN = to WINDOWAGG_PASSTHROUGH_STRICT mode. The bug is present in the latest STAB= LE branch. What's the Issue? During window function execution, the first window function is evaluated, a= nd the per-tuple result is stored in econtext->ecxt_aggvalues. For instance= , if a window function like lead is evaluated on varlena columns, the resul= t is copied to (econtext)->ecxt_per_tuple_memory. This memory is reset befo= re beginning window function evaluation for each row. When the run conditio= n fails for a row in pass-through mode, the window aggregate status changes= to WINDOWAGG_PASSTHROUGH_STRICT, and execution proceeds to the next tuple = if rows remain in the partition. During the next tuple's execution, (econtext)->ecxt_per_tuple_memory is res= et, and the window function is skipped due to WINDOWAGG_PASSTHROUGH_STRICT = mode. However, the ExecProject function still executes for the tuple to sto= re aggregate results in the result slot. Since econtext->ecxt_aggvalues is = not set to NULL after the run condition fails, it attempts to access the pr= evious tuple's window function result for the current row. When the window = function operates on varlena columns, this results in invalid memory access= at MakeExpandedObjectReadOnlyInternal(state->resvalue); (execExprInterp.c)= . This bug occurs immediately after a failed run condition, particularly when= using window functions like lead() on varlena columns. Question: Why does ExecProject still execute if the current window aggregat= e status is WINDOWAGG_PASSTHROUGH_STRICT mode? Possible Fixes 1) Skip the partition immediately after a run condition fails for a row, us= ing windowaggstatus and winstate->partition_spooled info to begin processin= g the next partition (windowagg_fix1.patch) 2) Set aggregate values to NULL upon run condition failure in WINDOWAGG_PAS= STHROUGH_STRICT mode (windowagg_fix2.patch) How was the Bug Detected? In our private project, we use a custom malloc allocator to enhance Address= Sanitizer's effectiveness in detecting memory-related bugs. Up to PostgreSQ= L v14, this allocator was implemented as a custom solution. However, starti= ng from version 16, PostgreSQL restricts the use of custom allocators. Cons= equently, we integrated this allocator directly into PostgreSQL and are usi= ng it to run regressions (malloc_allocator.patch) Our custom allocator precisely allocates the user's requested size, and pfr= ee operations immediately release pointers without adding them to any freel= ist. Though this approach incurs performance degradation, it allows Address= Sanitizer to detect use-after-free bugs more effectively. Why Aset Context Missed the Bug: The Aset context did not detect the bug because it did not immediately free= pointers, instead added them to a freelist. Steps to Reproduce the Issue Using Custom Malloc Allocator 1) Apply the attached malloc_allocator.patch to the master branch (#commit:= bfeeb065ea2c870cf4d9dfcd552d23d72432e692) and compile PG with the --enable= -asan=C2=A0flag. 2) Execute below SQL queries: create table issue(a int, f text); insert into issue values (1, 'aa'), (1, 'bb'); select * from ( select row_number() over (partition by a) as first, lead(f)= over (partition by a) as third from issue) emp where first < 1; NOTE: Running regressions with our custom allocator may reveal additional m= emory-related bugs or crashes in the master branch. Please let me know if you have any questions or would like further details. Thanks & Regards, Jayesh Dehankar Member Technical Staff ZOHO Corporation ------=_Part_6448_1577446894.1731511331918 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable =
Hi Developers,

We hav= e discovered a bug in PostgreSQL v16.3 related to a top-level window aggreg= ate with a partition-by clause. The issue occurs when the run condition fai= ls, causing the window aggregate status to change from WINDOWAGG_RUN to WIN= DOWAGG_PASSTHROUGH_STRICT mode. The bug is present in the latest STABLE bra= nch.


What's the Issue?

During window function execution, the first win= dow function is evaluated, and the per-tuple result is stored in econtext-&= gt;ecxt_aggvalues. For instance, if a window function like lead is evaluate= d on varlena columns, the result is copied to (econtext)->ecxt_per_tuple= _memory. This memory is reset before beginning window function evaluation f= or each row. When the run condition fails for a row in pass-through mode, t= he window aggregate status changes to WINDOWAGG_PASSTHROUGH_STRICT, and exe= cution proceeds to the next tuple if rows remain in the partition.

During the next tuple's execution, (econtext)->ecxt= _per_tuple_memory is reset, and the window function is skipped due to WINDO= WAGG_PASSTHROUGH_STRICT mode. However, the ExecProject function still execu= tes for the tuple to store aggregate results in the result slot. Since econ= text->ecxt_aggvalues is not set to NULL after the run condition fails, i= t attempts to access the previous tuple's window function result for the cu= rrent row. When the window function operates on varlena columns, this resul= ts in invalid memory access at MakeExpandedObjectReadOnlyInternal(state->= ;resvalue); (execExprInterp.c).

This bug occur= s immediately after a failed run condition, particularly when using window = functions like lead() on varlena columns.

Q= uestion: Why does ExecProject still execute if the current window aggre= gate status is WINDOWAGG_PASSTHROUGH_STRICT mode?

<= div>
Possible Fixes

1) Sk= ip the partition immediately after a run condition fails for a row, using w= indowaggstatus and winstate->partition_spooled info to begin processing = the next partition (windowagg_fix1.patch)

2) Set aggregat= e values to NULL upon run condition failure in WINDOWAGG_PASSTHROUGH_STRICT= mode (windowagg_fix2.patch)


How was the Bug Detected?

In our private = project, we use a custom malloc allocator to enhance AddressSanitizer's eff= ectiveness in detecting memory-related bugs. Up to PostgreSQL v14, this all= ocator was implemented as a custom solution. However, starting from version= 16, PostgreSQL restricts the use of custom allocators. Consequently, we in= tegrated this allocator directly into PostgreSQL and are using it to run re= gressions (malloc_allocator.patch)

Our custom = allocator precisely allocates the user's requested size, and pfree operatio= ns immediately release pointers without adding them to any freelist. Though= this approach incurs performance degradation, it allows AddressSanitizer t= o detect use-after-free bugs more effectively.

Why Aset Context Missed the Bug:
The Aset context did= not detect the bug because it did not immediately free pointers, instead a= dded them to a freelist.


Ste= ps to Reproduce the Issue Using Custom Malloc Allocator
<= br>
1) Apply the attached malloc_allocator.patch to the master br= anch (#commit: bfeeb065ea2c870cf4d9dfcd552d23d72432e692) and compile PG wit= h the --enable-asan flag.

2) = Execute below SQL queries:
create table issu= e(a int, f text);
insert into issue values (1, 'aa'), (1, 'bb= ');
select * from ( select row_number() over (partition by a)= as first, lead(f) over (partition by a) as third from issue) emp where fir= st < 1;

= NOTE: Running regressions with our custom allocator may reveal a= dditional memory-related bugs or crashes in the master branch.

Please let me know if you have any questions or would like= further details.


Thanks & Regards,Jayesh Dehankar
Member Technical Staff
ZOHO Corporation
<= /div>
------=_Part_6448_1577446894.1731511331918-- ------=_Part_6447_1211805977.1731511331918 Content-Type: application/octet-stream; name=windowagg_fix1.patch Content-Transfer-Encoding: 7bit X-ZM_AttachId: 139508041319180110 Content-Disposition: attachment; filename=windowagg_fix1.patch