diff --git a/SOURCES/rpm-4.11.x-improve-sig-header-range-checks.patch b/SOURCES/rpm-4.11.x-improve-sig-header-range-checks.patch
new file mode 100644
index 0000000..c856272
--- /dev/null
+++ b/SOURCES/rpm-4.11.x-improve-sig-header-range-checks.patch
@@ -0,0 +1,278 @@
+commit d6a86b5e69e46cc283b1e06c92343319beb42e21
+Author: Panu Matilainen <pmatilai@redhat.com>
+Date:   Thu Mar 4 13:21:19 2021 +0200
+
+    Be much more careful about copying data from the signature header
+    
+    Only look for known tags, and ensure correct type and size where known
+    before copying over. Bump the old arbitrary 16k count limit to 16M limit
+    though, it's not inconceivable that a package could have that many files.
+    While at it, ensure none of these tags exist in the main header,
+    which would confuse us greatly.
+    
+    This is optimized for backporting ease, upstream can remove redundancies
+    and further improve checking later.
+    
+    Reported and initial patches by Demi Marie Obenour.
+    
+    Fixes: RhBug:1935049, RhBug:1933867, RhBug:1935035, RhBug:1934125, ...
+    
+    Fixes: CVE-2021-3421, CVE-2021-20271
+
+    Backported into 4.11.3 and combined with upstream commits:
+      - f7b97593af5cf818a5c6c5b9bc55bba6d08c9cb0
+      - e2f1f1931c5ccf3ecbe4e1e12cacb1e17a277776
+      - 822c3dc2046c29718e34ac2da16a9757a9be11da
+
+diff --git a/lib/package.c b/lib/package.c
+index 985ac47d3..565e4bf6d 100644
+--- a/lib/package.c
++++ b/lib/package.c
+@@ -28,79 +28,75 @@ static unsigned int * keyids;
+ extern int auditGpgResult;
+ extern int auditEnabled;
+ 
++static struct taglate_s {
++    rpmTagVal stag;
++    rpmTagVal xtag;
++    rpm_count_t count;
++    int quirk;
++} const xlateTags[] = {
++    { RPMSIGTAG_SIZE, RPMTAG_SIGSIZE, 1, 0 },
++    { RPMSIGTAG_PGP, RPMTAG_SIGPGP, 0, 0 },
++    { RPMSIGTAG_MD5, RPMTAG_SIGMD5, 16, 0 },
++    { RPMSIGTAG_GPG, RPMTAG_SIGGPG, 0, 0 },
++    /* { RPMSIGTAG_PGP5, RPMTAG_SIGPGP5, 0, 0 }, */ /* long obsolete, dont use */
++    { RPMSIGTAG_PAYLOADSIZE, RPMTAG_ARCHIVESIZE, 1, 1 },
++    { RPMSIGTAG_SHA1, RPMTAG_SHA1HEADER, 1, 0 },
++    { RPMSIGTAG_DSA, RPMTAG_DSAHEADER, 0, 0 },
++    { RPMSIGTAG_RSA, RPMTAG_RSAHEADER, 0, 0 },
++    { RPMSIGTAG_LONGSIZE, RPMTAG_LONGSIGSIZE, 1, 0 },
++    { RPMSIGTAG_LONGARCHIVESIZE, RPMTAG_LONGARCHIVESIZE, 1, 0 },
++    { 0 }
++};
++
+ /** \ingroup header
+  * Translate and merge legacy signature tags into header.
+  * @param h		header (dest)
+  * @param sigh		signature header (src)
++ * @return		failing tag number, 0 on success
+  */
+-static void headerMergeLegacySigs(Header h, Header sigh)
++static
++rpmTagVal headerMergeLegacySigs(Header h, Header sigh, char **msg)
+ {
+-    HeaderIterator hi;
++    const struct taglate_s *xl;
+     struct rpmtd_s td;
+ 
+-    hi = headerInitIterator(sigh);
+-    for (; headerNext(hi, &td); rpmtdFreeData(&td))
+-    {
+-	switch (td.tag) {
+-	/* XXX Translate legacy signature tag values. */
+-	case RPMSIGTAG_SIZE:
+-	    td.tag = RPMTAG_SIGSIZE;
+-	    break;
+-	case RPMSIGTAG_PGP:
+-	    td.tag = RPMTAG_SIGPGP;
+-	    break;
+-	case RPMSIGTAG_MD5:
+-	    td.tag = RPMTAG_SIGMD5;
+-	    break;
+-	case RPMSIGTAG_GPG:
+-	    td.tag = RPMTAG_SIGGPG;
+-	    break;
+-	case RPMSIGTAG_PGP5:
+-	    td.tag = RPMTAG_SIGPGP5;
+-	    break;
+-	case RPMSIGTAG_PAYLOADSIZE:
+-	    td.tag = RPMTAG_ARCHIVESIZE;
+-	    break;
+-	case RPMSIGTAG_SHA1:
+-	case RPMSIGTAG_DSA:
+-	case RPMSIGTAG_RSA:
+-	default:
+-	    if (!(td.tag >= HEADER_SIGBASE && td.tag < HEADER_TAGBASE))
++    for (xl = xlateTags; xl->stag; xl++) {
++	/* There mustn't be one in the main header */
++	if (headerIsEntry(h, xl->xtag)) {
++	    /* Some tags may exist in either header, but never both */
++	    if (xl->quirk && !headerIsEntry(sigh, xl->stag))
+ 		continue;
+-	    break;
++	    goto exit;
+ 	}
+-	if (td.data == NULL) continue;	/* XXX can't happen */
+-	if (!headerIsEntry(h, td.tag)) {
+-	    if (hdrchkType(td.type))
+-		continue;
+-	    if (td.count < 0 || hdrchkData(td.count))
+-		continue;
+-	    switch(td.type) {
+-	    case RPM_NULL_TYPE:
+-		continue;
++    }
++
++    rpmtdReset(&td);
++    for (xl = xlateTags; xl->stag; xl++) {
++	if (headerGet(sigh, xl->stag, &td, HEADERGET_RAW|HEADERGET_MINMEM)) {
++	    /* Translate legacy tags */
++	    if (xl->stag != xl->xtag)
++		td.tag = xl->xtag;
++	    /* Ensure type and tag size match expectations */
++	    if (td.type != rpmTagGetTagType(td.tag))
+ 		break;
+-	    case RPM_CHAR_TYPE:
+-	    case RPM_INT8_TYPE:
+-	    case RPM_INT16_TYPE:
+-	    case RPM_INT32_TYPE:
+-	    case RPM_INT64_TYPE:
+-		if (td.count != 1)
+-		    continue;
++	    if (td.count < 1 || td.count > 16*1024*1024)
+ 		break;
+-	    case RPM_STRING_TYPE:
+-	    case RPM_BIN_TYPE:
+-		if (td.count >= 16*1024)
+-		    continue;
++	    if (xl->count && td.count != xl->count)
+ 		break;
+-	    case RPM_STRING_ARRAY_TYPE:
+-	    case RPM_I18NSTRING_TYPE:
+-		continue;
++	    if (!headerPut(h, &td, HEADERPUT_DEFAULT))
+ 		break;
+-	    }
+-	    (void) headerPut(h, &td, HEADERPUT_DEFAULT);
++	    rpmtdFreeData(&td);
+ 	}
+     }
+-    headerFreeIterator(hi);
++    rpmtdFreeData(&td);
++
++exit:
++    if (xl->stag) {
++	rasprintf(msg, "invalid signature tag %s (%d)",
++			rpmTagGetName(xl->xtag), xl->xtag);
++    }
++
++    return xl->stag;
+ }
+ 
+ /**
+@@ -671,7 +667,7 @@ static rpmRC rpmpkgRead(rpmKeyring keyring, rpmVSFlags vsflags,
+ 	rpmlog(RPMLOG_ERR, "%s: %s", fn, msg);
+ 	break;
+     }
+-    free(msg);
++    msg = _free(msg);
+ 
+ exit:
+     if (rc != RPMRC_FAIL && h != NULL && hdrp != NULL) {
+@@ -701,10 +697,13 @@ exit:
+ 	    headerConvert(h, HEADERCONV_COMPRESSFILELIST);
+ 	
+ 	/* Append (and remap) signature tags to the metadata. */
+-	headerMergeLegacySigs(h, sigh);
+-
+-	/* Bump reference count for return. */
+-	*hdrp = headerLink(h);
++	if (headerMergeLegacySigs(h, sigh, &msg)) {
++	    rpmlog(RPMLOG_ERR, "%s: %s\n", fn, msg);
++	    free(msg);
++	    rc = RPMRC_FAIL;
++	} else
++	    /* Bump reference count for return. */
++	    *hdrp = headerLink(h);
+     }
+     rpmtdFreeData(&sigtd);
+     rpmDigestFinal(ctx, NULL, NULL, 0);
+
+commit 441a0998fca9b3d01052bc3d33f8a72f725d5865
+Author: Panu Matilainen <pmatilai@redhat.com>
+Date:   Fri Oct 21 17:37:26 2016 +0300
+
+    Require exact match on header vs region sizes when reading package files
+    
+    In rpm V4 packages both the signature and mail header consist of one
+    contiguous immutable region when read from package files and any
+    disparities in index or data size means malformed package.
+    
+    Once the signature header is merged into the main header this is
+    no longer true of course and installation adds further tags, so for
+    headers from other sources (such as rpmdb), tags outside the immutable
+    region (known as "dribbles" in rpm lore) must be allowed, and eg
+    headerCheck() cannot require exact match.
+
+    Backported into 4.11.3.  Note that the upstream patch also enforces
+    exact_size for signature headers, but this was later reverted in commit
+    34c2ba3c6a80a778cdf2e42a9193b3264e08e1b3, so we omit that part here.
+
+diff --git a/lib/package.c b/lib/package.c
+index 08f0bc634..fea27e9ca 100644
+--- a/lib/package.c
++++ b/lib/package.c
+@@ -275,7 +275,8 @@ exit:
+ }
+ 
+ static rpmRC headerVerify(rpmKeyring keyring, rpmVSFlags vsflags,
+-			  const void * uh, size_t uc, char ** msg)
++			  const void * uh, size_t uc, int exact_size,
++			  char ** msg)
+ {
+     char *buf = NULL;
+     int32_t * ei = (int32_t *) uh;
+@@ -287,6 +288,7 @@ static rpmRC headerVerify(rpmKeyring keyring, rpmVSFlags vsflags,
+     struct indexEntry_s entry;
+     struct entryInfo_s info;
+     int32_t ril = 0;
++    int32_t rdl = 0;
+     unsigned char * regionEnd = NULL;
+     rpmRC rc = RPMRC_FAIL;	/* assume failure */
+ 
+@@ -337,6 +339,7 @@ static rpmRC headerVerify(rpmKeyring keyring, rpmVSFlags vsflags,
+     regionEnd = dataStart + entry.info.offset;
+     (void) memcpy(&info, regionEnd, REGION_TAG_COUNT);
+     regionEnd += REGION_TAG_COUNT;
++    rdl = regionEnd - dataStart;
+ 
+     if (headerVerifyInfo(1, il * sizeof(*pe) + REGION_TAG_COUNT, &info, &entry.info, 1) != -1 ||
+ 	!(entry.info.tag == RPMTAG_HEADERIMMUTABLE
+@@ -358,9 +361,17 @@ static rpmRC headerVerify(rpmKeyring keyring, rpmVSFlags vsflags,
+ 	goto exit;
+     }
+ 
++    /* In package files region size is expected to match header size. */
++    if (exact_size && !(il == ril && dl == rdl)) {
++	rasprintf(&buf,
++		_("region %d: tag number mismatch %d ril %d dl %d rdl %d\n"),
++		entry.info.tag, il, ril, dl, rdl);
++	goto exit;
++    }
++
+     /* Verify header-only digest/signature if there is one we can use. */
+     rc = headerSigVerify(keyring, vsflags,
+-			 il, dl, ril, (regionEnd - dataStart),
++			 il, dl, ril, rdl,
+ 			 pe, dataStart, &buf);
+ 
+ exit:
+@@ -394,7 +405,7 @@ rpmRC headerCheck(rpmts ts, const void * uh, size_t uc, char ** msg)
+     rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
+ 
+     rpmswEnter(rpmtsOp(ts, RPMTS_OP_DIGEST), 0);
+-    rc = headerVerify(keyring, vsflags, uh, uc, msg);
++    rc = headerVerify(keyring, vsflags, uh, uc, 0, msg);
+     rpmswExit(rpmtsOp(ts, RPMTS_OP_DIGEST), uc);
+     rpmKeyringFree(keyring);
+ 
+@@ -453,7 +464,7 @@ static rpmRC rpmpkgReadHeader(rpmKeyring keyring, rpmVSFlags vsflags,
+     }
+ 
+     /* Sanity check header tags */
+-    rc = headerVerify(keyring, vsflags, ei, uc, &buf);
++    rc = headerVerify(keyring, vsflags, ei, uc, 1, &buf);
+     if (rc != RPMRC_OK)
+ 	goto exit;
+ 
diff --git a/SPECS/rpm.spec b/SPECS/rpm.spec
index dccf4bb..cadf92e 100644
--- a/SPECS/rpm.spec
+++ b/SPECS/rpm.spec
@@ -21,7 +21,7 @@
 Summary: The RPM package management system
 Name: rpm
 Version: %{rpmver}
-Release: %{?snapver:0.%{snapver}.}46%{?dist}
+Release: %{?snapver:0.%{snapver}.}48%{?dist}
 Group: System Environment/Base
 Url: http://www.rpm.org/
 Source0: http://rpm.org/releases/rpm-4.11.x/%{name}-%{srcver}.tar.bz2
@@ -106,6 +106,7 @@ Patch201: rpm-4.11.x-do-not-filter-ld64.patch
 
 Patch202: rpm-4.14.x-PGP-handle-no-EOL-at-EOF.patch
 Patch203: rpm-4.11.x-bump-up-the-limit-of-signature-header-to-64MB.patch
+Patch204: rpm-4.11.x-improve-sig-header-range-checks.patch
 
 # These are not yet upstream
 Patch301: rpm-4.6.0-niagara.patch
@@ -397,6 +398,7 @@ Requires: rpm-libs%{_isa} = %{version}-%{release}
 
 %patch202 -p1 -b .PGP-no-EOL
 %patch203 -p1 -b .sig-limit-bump
+%patch204 -p1 -b .sig-hdr-check
 
 %patch301 -p1 -b .niagara
 %patch302 -p1 -b .geode
@@ -659,6 +661,13 @@ exit 0
 %doc COPYING doc/librpm/html/*
 
 %changelog
+* Mon Nov 01 2021 Michal Domonkos <mdomonko@redhat.com> - 4.11.3-48
+- Fix double-free in previously added patch (#2004228)
+
+* Thu Oct 21 2021 Michal Domonkos <mdomonko@redhat.com> - 4.11.3-47
+- Improve range checks on signature and main header tags (#2004228)
+- Fixes CVE-2021-20271
+
 * Fri Sep 17 2021 Michal Domonkos <mdomonko@redhat.com> - 4.11.3-46
 - Bump up the limit of signature header to 64MB (#1993242)