mirror of
https://git.yoctoproject.org/poky
synced 2026-02-20 16:39:40 +01:00
Compare commits
103 Commits
yocto-3.1.
...
yocto-3.1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dbe19706ec | ||
|
|
b86bf0103c | ||
|
|
bf9eabafc2 | ||
|
|
8c297c4b42 | ||
|
|
658a3832de | ||
|
|
0b105ed7c8 | ||
|
|
25972743e6 | ||
|
|
2e5bead98f | ||
|
|
7dd1d3973e | ||
|
|
9e0ebdc95f | ||
|
|
9716962e34 | ||
|
|
fd1ba91818 | ||
|
|
2b4a973db3 | ||
|
|
45845f8056 | ||
|
|
d0120e8aec | ||
|
|
97b8007eff | ||
|
|
59f99476d8 | ||
|
|
7f8394e2b1 | ||
|
|
24fe566155 | ||
|
|
b8f1972b84 | ||
|
|
75bc08971b | ||
|
|
c86e8fe287 | ||
|
|
3afde32bfa | ||
|
|
0efbcefe73 | ||
|
|
c64835823a | ||
|
|
716693cccc | ||
|
|
8cabed090e | ||
|
|
be8b0f8178 | ||
|
|
7c678246f6 | ||
|
|
d3f1ae99a7 | ||
|
|
d843ae7d5d | ||
|
|
63909c1cc6 | ||
|
|
4553984904 | ||
|
|
1149fde2f4 | ||
|
|
cdc9522ede | ||
|
|
800272477a | ||
|
|
6fd1064e82 | ||
|
|
76fb1012eb | ||
|
|
1b7c8a3e7e | ||
|
|
38c0d384bd | ||
|
|
01cabaea04 | ||
|
|
72c7bacfd3 | ||
|
|
8a8d40420f | ||
|
|
a405e12beb | ||
|
|
be04eefcaf | ||
|
|
77214fc5d4 | ||
|
|
cd9a699320 | ||
|
|
5e9e6627ac | ||
|
|
facedadb5c | ||
|
|
e66a386604 | ||
|
|
aeac103466 | ||
|
|
9a045bde41 | ||
|
|
6ebec3f39b | ||
|
|
9f0c1bc687 | ||
|
|
1d15d166d0 | ||
|
|
5c342965c4 | ||
|
|
6f375f52f5 | ||
|
|
52250009cd | ||
|
|
6f56a14cdc | ||
|
|
f1cf9f0f12 | ||
|
|
0d5e538519 | ||
|
|
374e198436 | ||
|
|
42c52c4f24 | ||
|
|
1a3807e0ca | ||
|
|
c7ddb9b198 | ||
|
|
ffbede6d58 | ||
|
|
2f34ea89db | ||
|
|
aa99487732 | ||
|
|
8ae21cd487 | ||
|
|
5a6d2dc704 | ||
|
|
f6f3a08371 | ||
|
|
cd97a607c6 | ||
|
|
f4cb2f5d52 | ||
|
|
3ee56c9b97 | ||
|
|
eebb034b21 | ||
|
|
e4d507b93b | ||
|
|
d01194c739 | ||
|
|
ee94c92957 | ||
|
|
5a893c6e96 | ||
|
|
43ffc2a5e7 | ||
|
|
a9d6f0c153 | ||
|
|
e3ae311c5f | ||
|
|
278d77034e | ||
|
|
c0535262c8 | ||
|
|
5ee2872089 | ||
|
|
f38a69e59f | ||
|
|
b731a65e4f | ||
|
|
da06aaf910 | ||
|
|
4d9612b31c | ||
|
|
cbb7afa601 | ||
|
|
f27e86a4d7 | ||
|
|
16c91216f1 | ||
|
|
e62c723b0c | ||
|
|
fe546dca3f | ||
|
|
00722bacdd | ||
|
|
15854dc716 | ||
|
|
f980ef9fec | ||
|
|
994a224d40 | ||
|
|
d32656ac24 | ||
|
|
669079e7c0 | ||
|
|
ea9b55c858 | ||
|
|
0734868d9d | ||
|
|
007a6e2dad |
24
SECURITY.md
Normal file
24
SECURITY.md
Normal file
@@ -0,0 +1,24 @@
|
||||
How to Report a Potential Vulnerability?
|
||||
========================================
|
||||
|
||||
If you would like to report a public issue (for example, one with a released
|
||||
CVE number), please report it using the
|
||||
[https://bugzilla.yoctoproject.org/enter_bug.cgi?product=Security Security Bugzilla].
|
||||
If you have a patch ready, submit it following the same procedure as any other
|
||||
patch as described in README.md.
|
||||
|
||||
If you are dealing with a not-yet released or urgent issue, please send a
|
||||
message to security AT yoctoproject DOT org, including as many details as
|
||||
possible: the layer or software module affected, the recipe and its version,
|
||||
and any example code, if available.
|
||||
|
||||
Branches maintained with security fixes
|
||||
---------------------------------------
|
||||
|
||||
See [https://wiki.yoctoproject.org/wiki/Stable_Release_and_LTS Stable release and LTS]
|
||||
for detailed info regarding the policies and maintenance of Stable branches.
|
||||
|
||||
The [https://wiki.yoctoproject.org/wiki/Releases Release page] contains a list of all
|
||||
releases of the Yocto Project. Versions in grey are no longer actively maintained with
|
||||
security patches, but well-tested patches may still be accepted for them for
|
||||
significant issues.
|
||||
24
bitbake/SECURITY.md
Normal file
24
bitbake/SECURITY.md
Normal file
@@ -0,0 +1,24 @@
|
||||
How to Report a Potential Vulnerability?
|
||||
========================================
|
||||
|
||||
If you would like to report a public issue (for example, one with a released
|
||||
CVE number), please report it using the
|
||||
[https://bugzilla.yoctoproject.org/enter_bug.cgi?product=Security Security Bugzilla].
|
||||
If you have a patch ready, submit it following the same procedure as any other
|
||||
patch as described in README.md.
|
||||
|
||||
If you are dealing with a not-yet released or urgent issue, please send a
|
||||
message to security AT yoctoproject DOT org, including as many details as
|
||||
possible: the layer or software module affected, the recipe and its version,
|
||||
and any example code, if available.
|
||||
|
||||
Branches maintained with security fixes
|
||||
---------------------------------------
|
||||
|
||||
See [https://wiki.yoctoproject.org/wiki/Stable_Release_and_LTS Stable release and LTS]
|
||||
for detailed info regarding the policies and maintenance of Stable branches.
|
||||
|
||||
The [https://wiki.yoctoproject.org/wiki/Releases Release page] contains a list of all
|
||||
releases of the Yocto Project. Versions in grey are no longer actively maintained with
|
||||
security patches, but well-tested patches may still be accepted for them for
|
||||
significant issues.
|
||||
@@ -229,9 +229,10 @@ class diskMonitor:
|
||||
freeInode = st.f_favail
|
||||
|
||||
if minInode and freeInode < minInode:
|
||||
# Some filesystems use dynamic inodes so can't run out
|
||||
# (e.g. btrfs). This is reported by the inode count being 0.
|
||||
if st.f_files == 0:
|
||||
# Some filesystems use dynamic inodes so can't run out.
|
||||
# This is reported by the inode count being 0 (btrfs) or the free
|
||||
# inode count being -1 (cephfs).
|
||||
if st.f_files == 0 or st.f_favail == -1:
|
||||
self.devDict[k][2] = None
|
||||
continue
|
||||
# Always show warning, the self.checked would always be False if the action is WARN
|
||||
|
||||
@@ -318,7 +318,8 @@ class SignatureGeneratorBasic(SignatureGenerator):
|
||||
else:
|
||||
sigfile = stampbase + "." + task + ".sigbasedata" + "." + self.basehash[tid]
|
||||
|
||||
bb.utils.mkdirhier(os.path.dirname(sigfile))
|
||||
with bb.utils.umask(0o002):
|
||||
bb.utils.mkdirhier(os.path.dirname(sigfile))
|
||||
|
||||
data = {}
|
||||
data['task'] = task
|
||||
|
||||
@@ -969,6 +969,17 @@ def which(path, item, direction = 0, history = False, executable=False):
|
||||
return "", hist
|
||||
return ""
|
||||
|
||||
@contextmanager
|
||||
def umask(new_mask):
|
||||
"""
|
||||
Context manager to set the umask to a specific mask, and restore it afterwards.
|
||||
"""
|
||||
current_mask = os.umask(new_mask)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.umask(current_mask)
|
||||
|
||||
def to_boolean(string, default=None):
|
||||
if not string:
|
||||
return default
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
DISTRO : "3.1.28"
|
||||
DISTRO : "3.1.30"
|
||||
DISTRO_NAME_NO_CAP : "dunfell"
|
||||
DISTRO_NAME : "Dunfell"
|
||||
DISTRO_NAME_NO_CAP_MINUS_ONE : "zeus"
|
||||
YOCTO_DOC_VERSION : "3.1.28"
|
||||
YOCTO_DOC_VERSION : "3.1.30"
|
||||
YOCTO_DOC_VERSION_MINUS_ONE : "3.0.4"
|
||||
DISTRO_REL_TAG : "yocto-3.1.28"
|
||||
DOCCONF_VERSION : "3.1.28"
|
||||
DISTRO_REL_TAG : "yocto-3.1.30"
|
||||
DOCCONF_VERSION : "3.1.30"
|
||||
BITBAKE_SERIES : "1.46"
|
||||
POKYVERSION : "23.0.28"
|
||||
POKYVERSION : "23.0.30"
|
||||
YOCTO_POKY : "poky-&DISTRO_NAME_NO_CAP;-&POKYVERSION;"
|
||||
YOCTO_DL_URL : "https://downloads.yoctoproject.org"
|
||||
YOCTO_AB_URL : "https://autobuilder.yoctoproject.org"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
DISTRO = "poky"
|
||||
DISTRO_NAME = "Poky (Yocto Project Reference Distro)"
|
||||
DISTRO_VERSION = "3.1.28"
|
||||
DISTRO_VERSION = "3.1.30"
|
||||
DISTRO_CODENAME = "dunfell"
|
||||
SDK_VENDOR = "-pokysdk"
|
||||
SDK_VERSION = "${@d.getVar('DISTRO_VERSION').replace('snapshot-${DATE}', 'snapshot')}"
|
||||
|
||||
@@ -97,6 +97,8 @@ def generate_json_report(d, out_path, link_path):
|
||||
cve_check_merge_jsons(summary, data)
|
||||
filename = f.readline()
|
||||
|
||||
summary["package"].sort(key=lambda d: d['name'])
|
||||
|
||||
with open(out_path, "w") as f:
|
||||
json.dump(summary, f, indent=2)
|
||||
|
||||
|
||||
@@ -405,8 +405,8 @@ kernel_do_install() {
|
||||
unset CFLAGS CPPFLAGS CXXFLAGS LDFLAGS MACHINE
|
||||
if (grep -q -i -e '^CONFIG_MODULES=y$' .config); then
|
||||
oe_runmake DEPMOD=echo MODLIB=${D}${nonarch_base_libdir}/modules/${KERNEL_VERSION} INSTALL_FW_PATH=${D}${nonarch_base_libdir}/firmware modules_install
|
||||
rm "${D}${nonarch_base_libdir}/modules/${KERNEL_VERSION}/build"
|
||||
rm "${D}${nonarch_base_libdir}/modules/${KERNEL_VERSION}/source"
|
||||
rm -f "${D}${nonarch_base_libdir}/modules/${KERNEL_VERSION}/build"
|
||||
rm -f "${D}${nonarch_base_libdir}/modules/${KERNEL_VERSION}/source"
|
||||
# If the kernel/ directory is empty remove it to prevent QA issues
|
||||
rmdir --ignore-fail-on-non-empty "${D}${nonarch_base_libdir}/modules/${KERNEL_VERSION}/kernel"
|
||||
else
|
||||
|
||||
@@ -75,7 +75,7 @@ def cve_check_merge_jsons(output, data):
|
||||
|
||||
for product in output["package"]:
|
||||
if product["name"] == data["package"][0]["name"]:
|
||||
bb.error("Error adding the same package twice")
|
||||
bb.error("Error adding the same package %s twice" % product["name"])
|
||||
return
|
||||
|
||||
output["package"].append(data["package"][0])
|
||||
@@ -114,11 +114,6 @@ def get_patched_cves(d):
|
||||
for url in oe.patch.src_patches(d):
|
||||
patch_file = bb.fetch.decodeurl(url)[2]
|
||||
|
||||
# Remote compressed patches may not be unpacked, so silently ignore them
|
||||
if not os.path.isfile(patch_file):
|
||||
bb.warn("%s does not exist, cannot extract CVE list" % patch_file)
|
||||
continue
|
||||
|
||||
# Check patch file name for CVE ID
|
||||
fname_match = cve_file_name_match.search(patch_file)
|
||||
if fname_match:
|
||||
@@ -126,6 +121,12 @@ def get_patched_cves(d):
|
||||
patched_cves.add(cve)
|
||||
bb.debug(2, "Found CVE %s from patch file name %s" % (cve, patch_file))
|
||||
|
||||
# Remote patches won't be present and compressed patches won't be
|
||||
# unpacked, so say we're not scanning them
|
||||
if not os.path.isfile(patch_file):
|
||||
bb.note("%s is remote or compressed, not scanning content" % patch_file)
|
||||
continue
|
||||
|
||||
with open(patch_file, "r", encoding="utf-8") as f:
|
||||
try:
|
||||
patch_text = f.read()
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import oe.path
|
||||
import oe.types
|
||||
|
||||
@@ -24,7 +27,6 @@ class CmdError(bb.BBHandledException):
|
||||
|
||||
|
||||
def runcmd(args, dir = None):
|
||||
import pipes
|
||||
import subprocess
|
||||
|
||||
if dir:
|
||||
@@ -35,7 +37,7 @@ def runcmd(args, dir = None):
|
||||
# print("cwd: %s -> %s" % (olddir, dir))
|
||||
|
||||
try:
|
||||
args = [ pipes.quote(str(arg)) for arg in args ]
|
||||
args = [ shlex.quote(str(arg)) for arg in args ]
|
||||
cmd = " ".join(args)
|
||||
# print("cmd: %s" % cmd)
|
||||
(exitstatus, output) = subprocess.getstatusoutput(cmd)
|
||||
|
||||
@@ -185,14 +185,8 @@ class TestImage(OESelftestTestCase):
|
||||
self.skipTest('virgl isn\'t working with Centos 7')
|
||||
if distro and distro == 'centos-8':
|
||||
self.skipTest('virgl isn\'t working with Centos 8')
|
||||
if distro and distro == 'fedora-34':
|
||||
self.skipTest('virgl isn\'t working with Fedora 34')
|
||||
if distro and distro == 'fedora-35':
|
||||
self.skipTest('virgl isn\'t working with Fedora 35')
|
||||
if distro and distro == 'fedora-36':
|
||||
self.skipTest('virgl isn\'t working with Fedora 36')
|
||||
if distro and distro == 'fedora-37':
|
||||
self.skipTest('virgl isn\'t working with Fedora 37')
|
||||
if distro and distro.startswith('fedora'):
|
||||
self.skipTest('virgl isn\'t working with Fedora')
|
||||
if distro and distro == 'opensuseleap-15.0':
|
||||
self.skipTest('virgl isn\'t working with Opensuse 15.0')
|
||||
if distro and distro == 'ubuntu-22.04':
|
||||
|
||||
97
meta/recipes-bsp/grub/files/CVE-2023-4692.patch
Normal file
97
meta/recipes-bsp/grub/files/CVE-2023-4692.patch
Normal file
@@ -0,0 +1,97 @@
|
||||
From 43651027d24e62a7a463254165e1e46e42aecdea Mon Sep 17 00:00:00 2001
|
||||
From: Maxim Suhanov <dfirblog@gmail.com>
|
||||
Date: Mon, 28 Aug 2023 16:31:57 +0300
|
||||
Subject: [PATCH] fs/ntfs: Fix an OOB write when parsing the $ATTRIBUTE_LIST
|
||||
attribute for the $MFT file
|
||||
|
||||
When parsing an extremely fragmented $MFT file, i.e., the file described
|
||||
using the $ATTRIBUTE_LIST attribute, current NTFS code will reuse a buffer
|
||||
containing bytes read from the underlying drive to store sector numbers,
|
||||
which are consumed later to read data from these sectors into another buffer.
|
||||
|
||||
These sectors numbers, two 32-bit integers, are always stored at predefined
|
||||
offsets, 0x10 and 0x14, relative to first byte of the selected entry within
|
||||
the $ATTRIBUTE_LIST attribute. Usually, this won't cause any problem.
|
||||
|
||||
However, when parsing a specially-crafted file system image, this may cause
|
||||
the NTFS code to write these integers beyond the buffer boundary, likely
|
||||
causing the GRUB memory allocator to misbehave or fail. These integers contain
|
||||
values which are controlled by on-disk structures of the NTFS file system.
|
||||
|
||||
Such modification and resulting misbehavior may touch a memory range not
|
||||
assigned to the GRUB and owned by firmware or another EFI application/driver.
|
||||
|
||||
This fix introduces checks to ensure that these sector numbers are never
|
||||
written beyond the boundary.
|
||||
|
||||
Fixes: CVE-2023-4692
|
||||
|
||||
Reported-by: Maxim Suhanov <dfirblog@gmail.com>
|
||||
Signed-off-by: Maxim Suhanov <dfirblog@gmail.com>
|
||||
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
|
||||
|
||||
Upstream-Status: Backport [https://git.savannah.gnu.org/cgit/grub.git/commit/?id=43651027d24e62a7a463254165e1e46e42aecdea]
|
||||
CVE: CVE-2023-4692
|
||||
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
|
||||
---
|
||||
grub-core/fs/ntfs.c | 18 +++++++++++++++++-
|
||||
1 file changed, 17 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c
|
||||
index 2f34f76..c8d3683 100644
|
||||
--- a/grub-core/fs/ntfs.c
|
||||
+++ b/grub-core/fs/ntfs.c
|
||||
@@ -184,7 +184,7 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr)
|
||||
}
|
||||
if (at->attr_end)
|
||||
{
|
||||
- grub_uint8_t *pa;
|
||||
+ grub_uint8_t *pa, *pa_end;
|
||||
|
||||
at->emft_buf = grub_malloc (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR);
|
||||
if (at->emft_buf == NULL)
|
||||
@@ -209,11 +209,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr)
|
||||
}
|
||||
at->attr_nxt = at->edat_buf;
|
||||
at->attr_end = at->edat_buf + u32at (pa, 0x30);
|
||||
+ pa_end = at->edat_buf + n;
|
||||
}
|
||||
else
|
||||
{
|
||||
at->attr_nxt = at->attr_end + u16at (pa, 0x14);
|
||||
at->attr_end = at->attr_end + u32at (pa, 4);
|
||||
+ pa_end = at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR);
|
||||
}
|
||||
at->flags |= GRUB_NTFS_AF_ALST;
|
||||
while (at->attr_nxt < at->attr_end)
|
||||
@@ -230,6 +232,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr)
|
||||
at->flags |= GRUB_NTFS_AF_GPOS;
|
||||
at->attr_cur = at->attr_nxt;
|
||||
pa = at->attr_cur;
|
||||
+
|
||||
+ if ((pa >= pa_end) || (pa_end - pa < 0x18))
|
||||
+ {
|
||||
+ grub_error (GRUB_ERR_BAD_FS, "can\'t parse attribute list");
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
grub_set_unaligned32 ((char *) pa + 0x10,
|
||||
grub_cpu_to_le32 (at->mft->data->mft_start));
|
||||
grub_set_unaligned32 ((char *) pa + 0x14,
|
||||
@@ -240,6 +249,13 @@ find_attr (struct grub_ntfs_attr *at, grub_uint8_t attr)
|
||||
{
|
||||
if (*pa != attr)
|
||||
break;
|
||||
+
|
||||
+ if ((pa >= pa_end) || (pa_end - pa < 0x18))
|
||||
+ {
|
||||
+ grub_error (GRUB_ERR_BAD_FS, "can\'t parse attribute list");
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
if (read_attr
|
||||
(at, pa + 0x10,
|
||||
u32at (pa, 0x10) * (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR),
|
||||
--
|
||||
2.25.1
|
||||
|
||||
62
meta/recipes-bsp/grub/files/CVE-2023-4693.patch
Normal file
62
meta/recipes-bsp/grub/files/CVE-2023-4693.patch
Normal file
@@ -0,0 +1,62 @@
|
||||
From 0ed2458cc4eff6d9a9199527e2a0b6d445802f94 Mon Sep 17 00:00:00 2001
|
||||
From: Maxim Suhanov <dfirblog@gmail.com>
|
||||
Date: Mon, 28 Aug 2023 16:32:33 +0300
|
||||
Subject: [PATCH] fs/ntfs: Fix an OOB read when reading data from the resident
|
||||
$DATA attribute
|
||||
|
||||
When reading a file containing resident data, i.e., the file data is stored in
|
||||
the $DATA attribute within the NTFS file record, not in external clusters,
|
||||
there are no checks that this resident data actually fits the corresponding
|
||||
file record segment.
|
||||
|
||||
When parsing a specially-crafted file system image, the current NTFS code will
|
||||
read the file data from an arbitrary, attacker-chosen memory offset and of
|
||||
arbitrary, attacker-chosen length.
|
||||
|
||||
This allows an attacker to display arbitrary chunks of memory, which could
|
||||
contain sensitive information like password hashes or even plain-text,
|
||||
obfuscated passwords from BS EFI variables.
|
||||
|
||||
This fix implements a check to ensure that resident data is read from the
|
||||
corresponding file record segment only.
|
||||
|
||||
Fixes: CVE-2023-4693
|
||||
|
||||
Reported-by: Maxim Suhanov <dfirblog@gmail.com>
|
||||
Signed-off-by: Maxim Suhanov <dfirblog@gmail.com>
|
||||
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
|
||||
|
||||
Upstream-Status: Backport [https://git.savannah.gnu.org/gitweb/?p=grub.git;a=commit;h=0ed2458cc4eff6d9a9199527e2a0b6d445802f94]
|
||||
CVE: CVE-2023-4693
|
||||
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
|
||||
---
|
||||
grub-core/fs/ntfs.c | 13 ++++++++++++-
|
||||
1 file changed, 12 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c
|
||||
index c8d3683..4d1fe42 100644
|
||||
--- a/grub-core/fs/ntfs.c
|
||||
+++ b/grub-core/fs/ntfs.c
|
||||
@@ -401,7 +401,18 @@ read_data (struct grub_ntfs_attr *at, grub_uint8_t *pa, grub_uint8_t *dest,
|
||||
{
|
||||
if (ofs + len > u32at (pa, 0x10))
|
||||
return grub_error (GRUB_ERR_BAD_FS, "read out of range");
|
||||
- grub_memcpy (dest, pa + u32at (pa, 0x14) + ofs, len);
|
||||
+
|
||||
+ if (u32at (pa, 0x10) > (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR))
|
||||
+ return grub_error (GRUB_ERR_BAD_FS, "resident attribute too large");
|
||||
+
|
||||
+ if (pa >= at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR))
|
||||
+ return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range");
|
||||
+
|
||||
+ if (u16at (pa, 0x14) + u32at (pa, 0x10) >
|
||||
+ (grub_addr_t) at->mft->buf + (at->mft->data->mft_size << GRUB_NTFS_BLK_SHR) - (grub_addr_t) pa)
|
||||
+ return grub_error (GRUB_ERR_BAD_FS, "resident attribute out of range");
|
||||
+
|
||||
+ grub_memcpy (dest, pa + u16at (pa, 0x14) + ofs, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
--
|
||||
2.25.1
|
||||
|
||||
@@ -109,6 +109,8 @@ SRC_URI = "${GNU_MIRROR}/grub/grub-${PV}.tar.gz \
|
||||
file://CVE-2020-27749.patch \
|
||||
file://CVE-2021-20225.patch \
|
||||
file://CVE-2021-20233.patch \
|
||||
file://CVE-2023-4692.patch \
|
||||
file://CVE-2023-4693.patch \
|
||||
"
|
||||
SRC_URI[md5sum] = "5ce674ca6b2612d8939b9e6abed32934"
|
||||
SRC_URI[sha256sum] = "f10c85ae3e204dbaec39ae22fa3c5e99f0665417e91c2cb49b7e5031658ba6ea"
|
||||
|
||||
@@ -22,6 +22,15 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=2d5025d4aa3495befef8f17206a5b0a1 \
|
||||
SRC_URI = "https://github.com/lathiat/avahi/releases/download/v${PV}/avahi-${PV}.tar.gz \
|
||||
file://fix-CVE-2017-6519.patch \
|
||||
file://CVE-2021-3468.patch \
|
||||
file://CVE-2023-1981.patch \
|
||||
file://CVE-2023-38469-1.patch \
|
||||
file://CVE-2023-38469-2.patch \
|
||||
file://CVE-2023-38470-1.patch \
|
||||
file://CVE-2023-38470-2.patch \
|
||||
file://CVE-2023-38471-1.patch \
|
||||
file://CVE-2023-38471-2.patch \
|
||||
file://CVE-2023-38472.patch \
|
||||
file://CVE-2023-38473.patch \
|
||||
"
|
||||
|
||||
UPSTREAM_CHECK_URI = "https://github.com/lathiat/avahi/releases/"
|
||||
|
||||
60
meta/recipes-connectivity/avahi/files/CVE-2023-1981.patch
Normal file
60
meta/recipes-connectivity/avahi/files/CVE-2023-1981.patch
Normal file
@@ -0,0 +1,60 @@
|
||||
Backport of:
|
||||
|
||||
From a2696da2f2c50ac43b6c4903f72290d5c3fa9f6f Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
|
||||
Date: Thu, 17 Nov 2022 01:51:53 +0100
|
||||
Subject: [PATCH] Emit error if requested service is not found
|
||||
|
||||
It currently just crashes instead of replying with error. Check return
|
||||
value and emit error instead of passing NULL pointer to reply.
|
||||
|
||||
Fixes #375
|
||||
|
||||
Upstream-Status: Backport [import from ubuntu https://git.launchpad.net/ubuntu/+source/avahi/tree/debian/patches/CVE-2023-1981.patch?h=ubuntu/focal-security
|
||||
Upstream commit https://github.com/lathiat/avahi/commit/a2696da2f2c50ac43b6c4903f72290d5c3fa9f6f]
|
||||
CVE: CVE-2023-1981
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
avahi-daemon/dbus-protocol.c | 20 ++++++++++++++------
|
||||
1 file changed, 14 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/avahi-daemon/dbus-protocol.c
|
||||
+++ b/avahi-daemon/dbus-protocol.c
|
||||
@@ -391,10 +391,14 @@ static DBusHandlerResult msg_server_impl
|
||||
}
|
||||
|
||||
t = avahi_alternative_host_name(n);
|
||||
- avahi_dbus_respond_string(c, m, t);
|
||||
- avahi_free(t);
|
||||
-
|
||||
- return DBUS_HANDLER_RESULT_HANDLED;
|
||||
+ if (t) {
|
||||
+ avahi_dbus_respond_string(c, m, t);
|
||||
+ avahi_free(t);
|
||||
+
|
||||
+ return DBUS_HANDLER_RESULT_HANDLED;
|
||||
+ } else {
|
||||
+ return avahi_dbus_respond_error(c, m, AVAHI_ERR_NOT_FOUND, "Hostname not found");
|
||||
+ }
|
||||
|
||||
} else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "GetAlternativeServiceName")) {
|
||||
char *n, *t;
|
||||
@@ -405,10 +409,14 @@ static DBusHandlerResult msg_server_impl
|
||||
}
|
||||
|
||||
t = avahi_alternative_service_name(n);
|
||||
- avahi_dbus_respond_string(c, m, t);
|
||||
- avahi_free(t);
|
||||
-
|
||||
- return DBUS_HANDLER_RESULT_HANDLED;
|
||||
+ if (t) {
|
||||
+ avahi_dbus_respond_string(c, m, t);
|
||||
+ avahi_free(t);
|
||||
+
|
||||
+ return DBUS_HANDLER_RESULT_HANDLED;
|
||||
+ } else {
|
||||
+ return avahi_dbus_respond_error(c, m, AVAHI_ERR_NOT_FOUND, "Service not found");
|
||||
+ }
|
||||
|
||||
} else if (dbus_message_is_method_call(m, AVAHI_DBUS_INTERFACE_SERVER, "EntryGroupNew")) {
|
||||
Client *client;
|
||||
48
meta/recipes-connectivity/avahi/files/CVE-2023-38469-1.patch
Normal file
48
meta/recipes-connectivity/avahi/files/CVE-2023-38469-1.patch
Normal file
@@ -0,0 +1,48 @@
|
||||
From a337a1ba7d15853fb56deef1f464529af6e3a1cf Mon Sep 17 00:00:00 2001
|
||||
From: Evgeny Vereshchagin <evvers@ya.ru>
|
||||
Date: Mon, 23 Oct 2023 20:29:31 +0000
|
||||
Subject: [PATCH] core: reject overly long TXT resource records
|
||||
|
||||
Closes https://github.com/lathiat/avahi/issues/455
|
||||
|
||||
CVE-2023-38469
|
||||
|
||||
Upstream-Status: Backport [import from ubuntu https://git.launchpad.net/ubuntu/+source/avahi/tree/debian/patches/CVE-2023-38469-1.patch?h=ubuntu/focal-security
|
||||
Upstream commit https://github.com/lathiat/avahi/commit/a337a1ba7d15853fb56deef1f464529af6e3a1cf]
|
||||
CVE: CVE-2023-38469
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
avahi-core/rr.c | 9 ++++++++-
|
||||
1 file changed, 8 insertions(+), 1 deletion(-)
|
||||
|
||||
Index: avahi-0.7/avahi-core/rr.c
|
||||
===================================================================
|
||||
--- avahi-0.7.orig/avahi-core/rr.c
|
||||
+++ avahi-0.7/avahi-core/rr.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <avahi-common/malloc.h>
|
||||
#include <avahi-common/defs.h>
|
||||
|
||||
+#include "dns.h"
|
||||
#include "rr.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
@@ -688,11 +689,17 @@ int avahi_record_is_valid(AvahiRecord *r
|
||||
case AVAHI_DNS_TYPE_TXT: {
|
||||
|
||||
AvahiStringList *strlst;
|
||||
+ size_t used = 0;
|
||||
|
||||
- for (strlst = r->data.txt.string_list; strlst; strlst = strlst->next)
|
||||
+ for (strlst = r->data.txt.string_list; strlst; strlst = strlst->next) {
|
||||
if (strlst->size > 255 || strlst->size <= 0)
|
||||
return 0;
|
||||
|
||||
+ used += 1+strlst->size;
|
||||
+ if (used > AVAHI_DNS_RDATA_MAX)
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
65
meta/recipes-connectivity/avahi/files/CVE-2023-38469-2.patch
Normal file
65
meta/recipes-connectivity/avahi/files/CVE-2023-38469-2.patch
Normal file
@@ -0,0 +1,65 @@
|
||||
From c6cab87df290448a63323c8ca759baa516166237 Mon Sep 17 00:00:00 2001
|
||||
From: Evgeny Vereshchagin <evvers@ya.ru>
|
||||
Date: Wed, 25 Oct 2023 18:15:42 +0000
|
||||
Subject: [PATCH] tests: pass overly long TXT resource records
|
||||
|
||||
to make sure they don't crash avahi any more.
|
||||
It reproduces https://github.com/lathiat/avahi/issues/455
|
||||
|
||||
Canonical notes:
|
||||
nickgalanis> removed first hunk since there is no .github dir in this release
|
||||
|
||||
Upstream-Status: Backport [import from ubuntu https://git.launchpad.net/ubuntu/+source/avahi/tree/debian/patches/CVE-2023-38469-2.patch?h=ubuntu/focal-security
|
||||
Upstream commit https://github.com/lathiat/avahi/commit/c6cab87df290448a63323c8ca759baa516166237]
|
||||
CVE: CVE-2023-38469
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
avahi-client/client-test.c | 14 ++++++++++++++
|
||||
1 files changed, 14 insertions(+)
|
||||
|
||||
Index: avahi-0.7/avahi-client/client-test.c
|
||||
===================================================================
|
||||
--- avahi-0.7.orig/avahi-client/client-test.c
|
||||
+++ avahi-0.7/avahi-client/client-test.c
|
||||
@@ -22,6 +22,7 @@
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
+#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <avahi-client/client.h>
|
||||
@@ -33,6 +34,8 @@
|
||||
#include <avahi-common/malloc.h>
|
||||
#include <avahi-common/timeval.h>
|
||||
|
||||
+#include <avahi-core/dns.h>
|
||||
+
|
||||
static const AvahiPoll *poll_api = NULL;
|
||||
static AvahiSimplePoll *simple_poll = NULL;
|
||||
|
||||
@@ -222,6 +225,9 @@ int main (AVAHI_GCC_UNUSED int argc, AVA
|
||||
uint32_t cookie;
|
||||
struct timeval tv;
|
||||
AvahiAddress a;
|
||||
+ uint8_t rdata[AVAHI_DNS_RDATA_MAX+1];
|
||||
+ AvahiStringList *txt = NULL;
|
||||
+ int r;
|
||||
|
||||
simple_poll = avahi_simple_poll_new();
|
||||
poll_api = avahi_simple_poll_get(simple_poll);
|
||||
@@ -258,6 +264,14 @@ int main (AVAHI_GCC_UNUSED int argc, AVA
|
||||
printf("%s\n", avahi_strerror(avahi_entry_group_add_service (group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, "Lathiat's Site", "_http._tcp", NULL, NULL, 80, "foo=bar", NULL)));
|
||||
printf("add_record: %d\n", avahi_entry_group_add_record (group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, "TestX", 0x01, 0x10, 120, "\5booya", 6));
|
||||
|
||||
+ memset(rdata, 1, sizeof(rdata));
|
||||
+ r = avahi_string_list_parse(rdata, sizeof(rdata), &txt);
|
||||
+ assert(r >= 0);
|
||||
+ assert(avahi_string_list_serialize(txt, NULL, 0) == sizeof(rdata));
|
||||
+ error = avahi_entry_group_add_service_strlst(group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, "TestX", "_qotd._tcp", NULL, NULL, 123, txt);
|
||||
+ assert(error == AVAHI_ERR_INVALID_RECORD);
|
||||
+ avahi_string_list_free(txt);
|
||||
+
|
||||
avahi_entry_group_commit (group);
|
||||
|
||||
domain = avahi_domain_browser_new (avahi, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, avahi_domain_browser_callback, (char*) "omghai3u");
|
||||
57
meta/recipes-connectivity/avahi/files/CVE-2023-38470-1.patch
Normal file
57
meta/recipes-connectivity/avahi/files/CVE-2023-38470-1.patch
Normal file
@@ -0,0 +1,57 @@
|
||||
From 94cb6489114636940ac683515417990b55b5d66c Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <pemensik@redhat.com>
|
||||
Date: Tue, 11 Apr 2023 15:29:59 +0200
|
||||
Subject: [PATCH] Ensure each label is at least one byte long
|
||||
|
||||
The only allowed exception is single dot, where it should return empty
|
||||
string.
|
||||
|
||||
Fixes #454.
|
||||
|
||||
Upstream-Status: Backport [import from ubuntu https://git.launchpad.net/ubuntu/+source/avahi/tree/debian/patches/CVE-2023-38470-1.patch?h=ubuntu/focal-security
|
||||
Upstream commit https://github.com/lathiat/avahi/commit/94cb6489114636940ac683515417990b55b5d66c]
|
||||
CVE: CVE-2023-38470
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
avahi-common/domain-test.c | 14 ++++++++++++++
|
||||
avahi-common/domain.c | 2 +-
|
||||
2 files changed, 15 insertions(+), 1 deletion(-)
|
||||
|
||||
Index: avahi-0.7/avahi-common/domain-test.c
|
||||
===================================================================
|
||||
--- avahi-0.7.orig/avahi-common/domain-test.c
|
||||
+++ avahi-0.7/avahi-common/domain-test.c
|
||||
@@ -45,6 +45,20 @@ int main(AVAHI_GCC_UNUSED int argc, AVAH
|
||||
printf("%s\n", s = avahi_normalize_name_strdup("fo\\\\o\\..f oo."));
|
||||
avahi_free(s);
|
||||
|
||||
+ printf("%s\n", s = avahi_normalize_name_strdup("."));
|
||||
+ avahi_free(s);
|
||||
+
|
||||
+ s = avahi_normalize_name_strdup(",.=.}.=.?-.}.=.?.?.}.}.?.?.?.z.?.?.}.}."
|
||||
+ "}.?.?.?.r.=.=.}.=.?.}}.}.?.?.?.zM.=.=.?.?.}.}.?.?.}.}.}"
|
||||
+ ".?.?.?.r.=.=.}.=.?.}}.}.?.?.?.zM.=.=.?.?.}.}.?.?.?.zM.?`"
|
||||
+ "?.}.}.}.?.?.?.r.=.?.}.=.?.?.}.?.?.?.}.=.?.?.}??.}.}.?.?."
|
||||
+ "?.z.?.?.}.}.}.?.?.?.r.=.=.}.=.?.}}.}.?.?.?.zM.?`?.}.}.}."
|
||||
+ "??.?.zM.?`?.}.}.}.?.?.?.r.=.?.}.=.?.?.}.?.?.?.}.=.?.?.}?"
|
||||
+ "?.}.}.?.?.?.z.?.?.}.}.}.?.?.?.r.=.=.}.=.?.}}.}.?.?.?.zM."
|
||||
+ "?`?.}.}.}.?.?.?.r.=.=.?.?`.?.?}.}.}.?.?.?.r.=.?.}.=.?.?."
|
||||
+ "}.?.?.?.}.=.?.?.}");
|
||||
+ assert(s == NULL);
|
||||
+
|
||||
printf("%i\n", avahi_domain_equal("\\065aa bbb\\.\\046cc.cc\\\\.dee.fff.", "Aaa BBB\\.\\.cc.cc\\\\.dee.fff"));
|
||||
printf("%i\n", avahi_domain_equal("A", "a"));
|
||||
|
||||
Index: avahi-0.7/avahi-common/domain.c
|
||||
===================================================================
|
||||
--- avahi-0.7.orig/avahi-common/domain.c
|
||||
+++ avahi-0.7/avahi-common/domain.c
|
||||
@@ -201,7 +201,7 @@ char *avahi_normalize_name(const char *s
|
||||
}
|
||||
|
||||
if (!empty) {
|
||||
- if (size < 1)
|
||||
+ if (size < 2)
|
||||
return NULL;
|
||||
|
||||
*(r++) = '.';
|
||||
53
meta/recipes-connectivity/avahi/files/CVE-2023-38470-2.patch
Normal file
53
meta/recipes-connectivity/avahi/files/CVE-2023-38470-2.patch
Normal file
@@ -0,0 +1,53 @@
|
||||
From 20dec84b2480821704258bc908e7b2bd2e883b24 Mon Sep 17 00:00:00 2001
|
||||
From: Evgeny Vereshchagin <evvers@ya.ru>
|
||||
Date: Tue, 19 Sep 2023 03:21:25 +0000
|
||||
Subject: [PATCH] [common] bail out when escaped labels can't fit into ret
|
||||
|
||||
Fixes:
|
||||
```
|
||||
==93410==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7f9e76f14c16 at pc 0x00000047208d bp 0x7ffee90a6a00 sp 0x7ffee90a61c8
|
||||
READ of size 1110 at 0x7f9e76f14c16 thread T0
|
||||
#0 0x47208c in __interceptor_strlen (out/fuzz-domain+0x47208c) (BuildId: 731b20c1eef22c2104e75a6496a399b10cfc7cba)
|
||||
#1 0x534eb0 in avahi_strdup avahi/avahi-common/malloc.c:167:12
|
||||
#2 0x53862c in avahi_normalize_name_strdup avahi/avahi-common/domain.c:226:12
|
||||
```
|
||||
and
|
||||
```
|
||||
fuzz-domain: fuzz/fuzz-domain.c:38: int LLVMFuzzerTestOneInput(const uint8_t *, size_t): Assertion `avahi_domain_equal(s, t)' failed.
|
||||
==101571== ERROR: libFuzzer: deadly signal
|
||||
#0 0x501175 in __sanitizer_print_stack_trace (/home/vagrant/avahi/out/fuzz-domain+0x501175) (BuildId: 682bf6400aff9d41b64b6e2cc3ef5ad600216ea8)
|
||||
#1 0x45ad2c in fuzzer::PrintStackTrace() (/home/vagrant/avahi/out/fuzz-domain+0x45ad2c) (BuildId: 682bf6400aff9d41b64b6e2cc3ef5ad600216ea8)
|
||||
#2 0x43fc07 in fuzzer::Fuzzer::CrashCallback() (/home/vagrant/avahi/out/fuzz-domain+0x43fc07) (BuildId: 682bf6400aff9d41b64b6e2cc3ef5ad600216ea8)
|
||||
#3 0x7f1581d7ebaf (/lib64/libc.so.6+0x3dbaf) (BuildId: c9f62793b9e886eb1b95077d4f26fe2b4aa1ac25)
|
||||
#4 0x7f1581dcf883 in __pthread_kill_implementation (/lib64/libc.so.6+0x8e883) (BuildId: c9f62793b9e886eb1b95077d4f26fe2b4aa1ac25)
|
||||
#5 0x7f1581d7eafd in gsignal (/lib64/libc.so.6+0x3dafd) (BuildId: c9f62793b9e886eb1b95077d4f26fe2b4aa1ac25)
|
||||
#6 0x7f1581d6787e in abort (/lib64/libc.so.6+0x2687e) (BuildId: c9f62793b9e886eb1b95077d4f26fe2b4aa1ac25)
|
||||
#7 0x7f1581d6779a in __assert_fail_base.cold (/lib64/libc.so.6+0x2679a) (BuildId: c9f62793b9e886eb1b95077d4f26fe2b4aa1ac25)
|
||||
#8 0x7f1581d77186 in __assert_fail (/lib64/libc.so.6+0x36186) (BuildId: c9f62793b9e886eb1b95077d4f26fe2b4aa1ac25)
|
||||
#9 0x5344a4 in LLVMFuzzerTestOneInput /home/vagrant/avahi/fuzz/fuzz-domain.c:38:9
|
||||
```
|
||||
|
||||
It's a follow-up to 94cb6489114636940ac683515417990b55b5d66c
|
||||
|
||||
Upstream-Status: Backport [import from ubuntu https://git.launchpad.net/ubuntu/+source/avahi/tree/debian/patches/CVE-2023-38471-2.patch?h=ubuntu/focal-security
|
||||
Upstream commit https://github.com/lathiat/avahi/commit/20dec84b2480821704258bc908e7b2bd2e883b24]
|
||||
CVE: CVE-2023-38470 #Follow-up patch
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
avahi-common/domain.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
Index: avahi-0.7/avahi-common/domain.c
|
||||
===================================================================
|
||||
--- avahi-0.7.orig/avahi-common/domain.c
|
||||
+++ avahi-0.7/avahi-common/domain.c
|
||||
@@ -210,7 +210,8 @@ char *avahi_normalize_name(const char *s
|
||||
} else
|
||||
empty = 0;
|
||||
|
||||
- avahi_escape_label(label, strlen(label), &r, &size);
|
||||
+ if (!(avahi_escape_label(label, strlen(label), &r, &size)))
|
||||
+ return NULL;
|
||||
}
|
||||
|
||||
return ret_s;
|
||||
73
meta/recipes-connectivity/avahi/files/CVE-2023-38471-1.patch
Normal file
73
meta/recipes-connectivity/avahi/files/CVE-2023-38471-1.patch
Normal file
@@ -0,0 +1,73 @@
|
||||
From 894f085f402e023a98cbb6f5a3d117bd88d93b09 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Sekletar <msekleta@redhat.com>
|
||||
Date: Mon, 23 Oct 2023 13:38:35 +0200
|
||||
Subject: [PATCH] core: extract host name using avahi_unescape_label()
|
||||
|
||||
Previously we could create invalid escape sequence when we split the
|
||||
string on dot. For example, from valid host name "foo\\.bar" we have
|
||||
created invalid name "foo\\" and tried to set that as the host name
|
||||
which crashed the daemon.
|
||||
|
||||
Fixes #453
|
||||
|
||||
CVE-2023-38471
|
||||
|
||||
Upstream-Status: Backport [import from ubuntu https://git.launchpad.net/ubuntu/+source/avahi/tree/debian/patches/CVE-2023-38471-1.patch?h=ubuntu/focal-security
|
||||
Upstream commit https://github.com/lathiat/avahi/commit/894f085f402e023a98cbb6f5a3d117bd88d93b09]
|
||||
CVE: CVE-2023-38471
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
avahi-core/server.c | 27 +++++++++++++++++++++------
|
||||
1 file changed, 21 insertions(+), 6 deletions(-)
|
||||
|
||||
Index: avahi-0.7/avahi-core/server.c
|
||||
===================================================================
|
||||
--- avahi-0.7.orig/avahi-core/server.c
|
||||
+++ avahi-0.7/avahi-core/server.c
|
||||
@@ -1253,7 +1253,11 @@ static void update_fqdn(AvahiServer *s)
|
||||
}
|
||||
|
||||
int avahi_server_set_host_name(AvahiServer *s, const char *host_name) {
|
||||
- char *hn = NULL;
|
||||
+ char label_escaped[AVAHI_LABEL_MAX*4+1];
|
||||
+ char label[AVAHI_LABEL_MAX];
|
||||
+ char *hn = NULL, *h;
|
||||
+ size_t len;
|
||||
+
|
||||
assert(s);
|
||||
|
||||
AVAHI_CHECK_VALIDITY(s, !host_name || avahi_is_valid_host_name(host_name), AVAHI_ERR_INVALID_HOST_NAME);
|
||||
@@ -1263,17 +1267,28 @@ int avahi_server_set_host_name(AvahiServ
|
||||
else
|
||||
hn = avahi_normalize_name_strdup(host_name);
|
||||
|
||||
- hn[strcspn(hn, ".")] = 0;
|
||||
+ h = hn;
|
||||
+ if (!avahi_unescape_label((const char **)&hn, label, sizeof(label))) {
|
||||
+ avahi_free(h);
|
||||
+ return AVAHI_ERR_INVALID_HOST_NAME;
|
||||
+ }
|
||||
+
|
||||
+ avahi_free(h);
|
||||
|
||||
- if (avahi_domain_equal(s->host_name, hn) && s->state != AVAHI_SERVER_COLLISION) {
|
||||
- avahi_free(hn);
|
||||
+ h = label_escaped;
|
||||
+ len = sizeof(label_escaped);
|
||||
+ if (!avahi_escape_label(label, strlen(label), &h, &len))
|
||||
+ return AVAHI_ERR_INVALID_HOST_NAME;
|
||||
+
|
||||
+ if (avahi_domain_equal(s->host_name, label_escaped) && s->state != AVAHI_SERVER_COLLISION)
|
||||
return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
|
||||
- }
|
||||
|
||||
withdraw_host_rrs(s);
|
||||
|
||||
avahi_free(s->host_name);
|
||||
- s->host_name = hn;
|
||||
+ s->host_name = avahi_strdup(label_escaped);
|
||||
+ if (!s->host_name)
|
||||
+ return AVAHI_ERR_NO_MEMORY;
|
||||
|
||||
update_fqdn(s);
|
||||
|
||||
52
meta/recipes-connectivity/avahi/files/CVE-2023-38471-2.patch
Normal file
52
meta/recipes-connectivity/avahi/files/CVE-2023-38471-2.patch
Normal file
@@ -0,0 +1,52 @@
|
||||
From b675f70739f404342f7f78635d6e2dcd85a13460 Mon Sep 17 00:00:00 2001
|
||||
From: Evgeny Vereshchagin <evvers@ya.ru>
|
||||
Date: Tue, 24 Oct 2023 22:04:51 +0000
|
||||
Subject: [PATCH] core: return errors from avahi_server_set_host_name properly
|
||||
|
||||
It's a follow-up to 894f085f402e023a98cbb6f5a3d117bd88d93b09
|
||||
|
||||
Upstream-Status: Backport [import from ubuntu https://git.launchpad.net/ubuntu/+source/avahi/tree/debian/patches/CVE-2023-38471-2.patch?h=ubuntu/focal-security
|
||||
Upstream commit https://github.com/lathiat/avahi/commit/b675f70739f404342f7f78635d6e2dcd85a13460]
|
||||
CVE: CVE-2023-38471 #Follow-up Patch
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
avahi-core/server.c | 9 ++++++---
|
||||
1 file changed, 6 insertions(+), 3 deletions(-)
|
||||
|
||||
Index: avahi-0.7/avahi-core/server.c
|
||||
===================================================================
|
||||
--- avahi-0.7.orig/avahi-core/server.c
|
||||
+++ avahi-0.7/avahi-core/server.c
|
||||
@@ -1267,10 +1267,13 @@ int avahi_server_set_host_name(AvahiServ
|
||||
else
|
||||
hn = avahi_normalize_name_strdup(host_name);
|
||||
|
||||
+ if (!hn)
|
||||
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
|
||||
+
|
||||
h = hn;
|
||||
if (!avahi_unescape_label((const char **)&hn, label, sizeof(label))) {
|
||||
avahi_free(h);
|
||||
- return AVAHI_ERR_INVALID_HOST_NAME;
|
||||
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
|
||||
}
|
||||
|
||||
avahi_free(h);
|
||||
@@ -1278,7 +1281,7 @@ int avahi_server_set_host_name(AvahiServ
|
||||
h = label_escaped;
|
||||
len = sizeof(label_escaped);
|
||||
if (!avahi_escape_label(label, strlen(label), &h, &len))
|
||||
- return AVAHI_ERR_INVALID_HOST_NAME;
|
||||
+ return avahi_server_set_errno(s, AVAHI_ERR_INVALID_HOST_NAME);
|
||||
|
||||
if (avahi_domain_equal(s->host_name, label_escaped) && s->state != AVAHI_SERVER_COLLISION)
|
||||
return avahi_server_set_errno(s, AVAHI_ERR_NO_CHANGE);
|
||||
@@ -1288,7 +1291,7 @@ int avahi_server_set_host_name(AvahiServ
|
||||
avahi_free(s->host_name);
|
||||
s->host_name = avahi_strdup(label_escaped);
|
||||
if (!s->host_name)
|
||||
- return AVAHI_ERR_NO_MEMORY;
|
||||
+ return avahi_server_set_errno(s, AVAHI_ERR_NO_MEMORY);
|
||||
|
||||
update_fqdn(s);
|
||||
|
||||
45
meta/recipes-connectivity/avahi/files/CVE-2023-38472.patch
Normal file
45
meta/recipes-connectivity/avahi/files/CVE-2023-38472.patch
Normal file
@@ -0,0 +1,45 @@
|
||||
From b024ae5749f4aeba03478e6391687c3c9c8dee40 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Sekletar <msekleta@redhat.com>
|
||||
Date: Thu, 19 Oct 2023 17:36:44 +0200
|
||||
Subject: [PATCH] core: make sure there is rdata to process before parsing it
|
||||
|
||||
Fixes #452
|
||||
|
||||
CVE-2023-38472
|
||||
|
||||
Upstream-Status: Backport [import from ubuntu https://git.launchpad.net/ubuntu/+source/avahi/tree/debian/patches/CVE-2023-38472.patch?h=ubuntu/focal-security
|
||||
Upstream commit https://github.com/lathiat/avahi/commit/b024ae5749f4aeba03478e6391687c3c9c8dee40]
|
||||
CVE: CVE-2023-38472
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
avahi-client/client-test.c | 3 +++
|
||||
avahi-daemon/dbus-entry-group.c | 2 +-
|
||||
2 files changed, 4 insertions(+), 1 deletion(-)
|
||||
|
||||
Index: avahi-0.7/avahi-client/client-test.c
|
||||
===================================================================
|
||||
--- avahi-0.7.orig/avahi-client/client-test.c
|
||||
+++ avahi-0.7/avahi-client/client-test.c
|
||||
@@ -272,6 +272,9 @@ int main (AVAHI_GCC_UNUSED int argc, AVA
|
||||
assert(error == AVAHI_ERR_INVALID_RECORD);
|
||||
avahi_string_list_free(txt);
|
||||
|
||||
+ error = avahi_entry_group_add_record (group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, 0, "TestX", 0x01, 0x10, 120, "", 0);
|
||||
+ assert(error != AVAHI_OK);
|
||||
+
|
||||
avahi_entry_group_commit (group);
|
||||
|
||||
domain = avahi_domain_browser_new (avahi, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, AVAHI_DOMAIN_BROWSER_BROWSE, 0, avahi_domain_browser_callback, (char*) "omghai3u");
|
||||
Index: avahi-0.7/avahi-daemon/dbus-entry-group.c
|
||||
===================================================================
|
||||
--- avahi-0.7.orig/avahi-daemon/dbus-entry-group.c
|
||||
+++ avahi-0.7/avahi-daemon/dbus-entry-group.c
|
||||
@@ -340,7 +340,7 @@ DBusHandlerResult avahi_dbus_msg_entry_g
|
||||
if (!(r = avahi_record_new_full (name, clazz, type, ttl)))
|
||||
return avahi_dbus_respond_error(c, m, AVAHI_ERR_NO_MEMORY, NULL);
|
||||
|
||||
- if (avahi_rdata_parse (r, rdata, size) < 0) {
|
||||
+ if (!rdata || avahi_rdata_parse (r, rdata, size) < 0) {
|
||||
avahi_record_unref (r);
|
||||
return avahi_dbus_respond_error(c, m, AVAHI_ERR_INVALID_RDATA, NULL);
|
||||
}
|
||||
109
meta/recipes-connectivity/avahi/files/CVE-2023-38473.patch
Normal file
109
meta/recipes-connectivity/avahi/files/CVE-2023-38473.patch
Normal file
@@ -0,0 +1,109 @@
|
||||
From b448c9f771bada14ae8de175695a9729f8646797 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Sekletar <msekleta@redhat.com>
|
||||
Date: Wed, 11 Oct 2023 17:45:44 +0200
|
||||
Subject: [PATCH] common: derive alternative host name from its unescaped
|
||||
version
|
||||
|
||||
Normalization of input makes sure we don't have to deal with special
|
||||
cases like unescaped dot at the end of label.
|
||||
|
||||
Fixes #451 #487
|
||||
CVE-2023-38473
|
||||
|
||||
Upstream-Status: Backport [import from ubuntu https://git.launchpad.net/ubuntu/+source/avahi/tree/debian/patches/CVE-2023-38473.patch?h=ubuntu/focal-security
|
||||
Upstream commit https://github.com/lathiat/avahi/commit/b448c9f771bada14ae8de175695a9729f8646797]
|
||||
CVE: CVE-2023-38473
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
avahi-common/alternative-test.c | 3 +++
|
||||
avahi-common/alternative.c | 27 +++++++++++++++++++--------
|
||||
2 files changed, 22 insertions(+), 8 deletions(-)
|
||||
|
||||
Index: avahi-0.7/avahi-common/alternative-test.c
|
||||
===================================================================
|
||||
--- avahi-0.7.orig/avahi-common/alternative-test.c
|
||||
+++ avahi-0.7/avahi-common/alternative-test.c
|
||||
@@ -31,6 +31,9 @@ int main(AVAHI_GCC_UNUSED int argc, AVAH
|
||||
const char* const test_strings[] = {
|
||||
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXüüüüüüü",
|
||||
+ ").",
|
||||
+ "\\.",
|
||||
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\\\\",
|
||||
"gurke",
|
||||
"-",
|
||||
" #",
|
||||
Index: avahi-0.7/avahi-common/alternative.c
|
||||
===================================================================
|
||||
--- avahi-0.7.orig/avahi-common/alternative.c
|
||||
+++ avahi-0.7/avahi-common/alternative.c
|
||||
@@ -49,15 +49,20 @@ static void drop_incomplete_utf8(char *c
|
||||
}
|
||||
|
||||
char *avahi_alternative_host_name(const char *s) {
|
||||
+ char label[AVAHI_LABEL_MAX], alternative[AVAHI_LABEL_MAX*4+1];
|
||||
+ char *alt, *r, *ret;
|
||||
const char *e;
|
||||
- char *r;
|
||||
+ size_t len;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (!avahi_is_valid_host_name(s))
|
||||
return NULL;
|
||||
|
||||
- if ((e = strrchr(s, '-'))) {
|
||||
+ if (!avahi_unescape_label(&s, label, sizeof(label)))
|
||||
+ return NULL;
|
||||
+
|
||||
+ if ((e = strrchr(label, '-'))) {
|
||||
const char *p;
|
||||
|
||||
e++;
|
||||
@@ -74,19 +79,18 @@ char *avahi_alternative_host_name(const
|
||||
|
||||
if (e) {
|
||||
char *c, *m;
|
||||
- size_t l;
|
||||
int n;
|
||||
|
||||
n = atoi(e)+1;
|
||||
if (!(m = avahi_strdup_printf("%i", n)))
|
||||
return NULL;
|
||||
|
||||
- l = e-s-1;
|
||||
+ len = e-label-1;
|
||||
|
||||
- if (l >= AVAHI_LABEL_MAX-1-strlen(m)-1)
|
||||
- l = AVAHI_LABEL_MAX-1-strlen(m)-1;
|
||||
+ if (len >= AVAHI_LABEL_MAX-1-strlen(m)-1)
|
||||
+ len = AVAHI_LABEL_MAX-1-strlen(m)-1;
|
||||
|
||||
- if (!(c = avahi_strndup(s, l))) {
|
||||
+ if (!(c = avahi_strndup(label, len))) {
|
||||
avahi_free(m);
|
||||
return NULL;
|
||||
}
|
||||
@@ -100,7 +104,7 @@ char *avahi_alternative_host_name(const
|
||||
} else {
|
||||
char *c;
|
||||
|
||||
- if (!(c = avahi_strndup(s, AVAHI_LABEL_MAX-1-2)))
|
||||
+ if (!(c = avahi_strndup(label, AVAHI_LABEL_MAX-1-2)))
|
||||
return NULL;
|
||||
|
||||
drop_incomplete_utf8(c);
|
||||
@@ -109,6 +113,13 @@ char *avahi_alternative_host_name(const
|
||||
avahi_free(c);
|
||||
}
|
||||
|
||||
+ alt = alternative;
|
||||
+ len = sizeof(alternative);
|
||||
+ ret = avahi_escape_label(r, strlen(r), &alt, &len);
|
||||
+
|
||||
+ avahi_free(r);
|
||||
+ r = avahi_strdup(ret);
|
||||
+
|
||||
assert(avahi_is_valid_host_name(r));
|
||||
|
||||
return r;
|
||||
175
meta/recipes-connectivity/bind/bind/CVE-2023-3341.patch
Normal file
175
meta/recipes-connectivity/bind/bind/CVE-2023-3341.patch
Normal file
@@ -0,0 +1,175 @@
|
||||
From c4fac5ca98efd02fbaef43601627c7a3a09f5a71 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Andrews <marka@isc.org>
|
||||
Date: Tue, 20 Jun 2023 15:21:36 +1000
|
||||
Subject: [PATCH] Limit isccc_cc_fromwire recursion depth
|
||||
|
||||
Named and rndc do not need a lot of recursion so the depth is
|
||||
set to 10.
|
||||
|
||||
Taken from BIND 9.16.44 change.
|
||||
|
||||
Upstream-Status: Backport [https://gitlab.isc.org/isc-projects/bind9/-/commit/c4fac5ca98efd02fbaef43601627c7a3a09f5a71]
|
||||
CVE: CVE-2023-3341
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
lib/isccc/cc.c | 38 +++++++++++++++++++++++---------
|
||||
lib/isccc/include/isccc/result.h | 4 +++-
|
||||
lib/isccc/result.c | 4 +++-
|
||||
3 files changed, 34 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/lib/isccc/cc.c b/lib/isccc/cc.c
|
||||
index e012685..8eac3d6 100644
|
||||
--- a/lib/isccc/cc.c
|
||||
+++ b/lib/isccc/cc.c
|
||||
@@ -53,6 +53,10 @@
|
||||
|
||||
#define MAX_TAGS 256
|
||||
#define DUP_LIFETIME 900
|
||||
+#ifndef ISCCC_MAXDEPTH
|
||||
+#define ISCCC_MAXDEPTH \
|
||||
+ 10 /* Big enough for rndc which just sends a string each way. */
|
||||
+#endif
|
||||
|
||||
typedef isccc_sexpr_t *sexpr_ptr;
|
||||
|
||||
@@ -561,19 +565,25 @@ verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
|
||||
|
||||
static isc_result_t
|
||||
table_fromwire(isccc_region_t *source, isccc_region_t *secret,
|
||||
- uint32_t algorithm, isccc_sexpr_t **alistp);
|
||||
+ uint32_t algorithm, unsigned int depth, isccc_sexpr_t **alistp);
|
||||
|
||||
static isc_result_t
|
||||
-list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
|
||||
+list_fromwire(isccc_region_t *source, unsigned int depth,
|
||||
+ isccc_sexpr_t **listp);
|
||||
|
||||
static isc_result_t
|
||||
-value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep) {
|
||||
+value_fromwire(isccc_region_t *source, unsigned int depth,
|
||||
+ isccc_sexpr_t **valuep) {
|
||||
unsigned int msgtype;
|
||||
uint32_t len;
|
||||
isccc_sexpr_t *value;
|
||||
isccc_region_t active;
|
||||
isc_result_t result;
|
||||
|
||||
+ if (depth > ISCCC_MAXDEPTH) {
|
||||
+ return (ISCCC_R_MAXDEPTH);
|
||||
+ }
|
||||
+
|
||||
if (REGION_SIZE(*source) < 1 + 4)
|
||||
return (ISC_R_UNEXPECTEDEND);
|
||||
GET8(msgtype, source->rstart);
|
||||
@@ -591,9 +601,9 @@ value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep) {
|
||||
} else
|
||||
result = ISC_R_NOMEMORY;
|
||||
} else if (msgtype == ISCCC_CCMSGTYPE_TABLE)
|
||||
- result = table_fromwire(&active, NULL, 0, valuep);
|
||||
+ result = table_fromwire(&active, NULL, 0, depth + 1, valuep);
|
||||
else if (msgtype == ISCCC_CCMSGTYPE_LIST)
|
||||
- result = list_fromwire(&active, valuep);
|
||||
+ result = list_fromwire(&active, depth + 1, valuep);
|
||||
else
|
||||
result = ISCCC_R_SYNTAX;
|
||||
|
||||
@@ -602,7 +612,7 @@ value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep) {
|
||||
|
||||
static isc_result_t
|
||||
table_fromwire(isccc_region_t *source, isccc_region_t *secret,
|
||||
- uint32_t algorithm, isccc_sexpr_t **alistp)
|
||||
+ uint32_t algorithm, unsigned int depth, isccc_sexpr_t **alistp)
|
||||
{
|
||||
char key[256];
|
||||
uint32_t len;
|
||||
@@ -613,6 +623,10 @@ table_fromwire(isccc_region_t *source, isccc_region_t *secret,
|
||||
|
||||
REQUIRE(alistp != NULL && *alistp == NULL);
|
||||
|
||||
+ if (depth > ISCCC_MAXDEPTH) {
|
||||
+ return (ISCCC_R_MAXDEPTH);
|
||||
+ }
|
||||
+
|
||||
checksum_rstart = NULL;
|
||||
first_tag = true;
|
||||
alist = isccc_alist_create();
|
||||
@@ -628,7 +642,7 @@ table_fromwire(isccc_region_t *source, isccc_region_t *secret,
|
||||
GET_MEM(key, len, source->rstart);
|
||||
key[len] = '\0'; /* Ensure NUL termination. */
|
||||
value = NULL;
|
||||
- result = value_fromwire(source, &value);
|
||||
+ result = value_fromwire(source, depth + 1, &value);
|
||||
if (result != ISC_R_SUCCESS)
|
||||
goto bad;
|
||||
if (isccc_alist_define(alist, key, value) == NULL) {
|
||||
@@ -661,14 +675,18 @@ table_fromwire(isccc_region_t *source, isccc_region_t *secret,
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
-list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp) {
|
||||
+list_fromwire(isccc_region_t *source, unsigned int depth, isccc_sexpr_t **listp) {
|
||||
isccc_sexpr_t *list, *value;
|
||||
isc_result_t result;
|
||||
|
||||
+ if (depth > ISCCC_MAXDEPTH) {
|
||||
+ return (ISCCC_R_MAXDEPTH);
|
||||
+ }
|
||||
+
|
||||
list = NULL;
|
||||
while (!REGION_EMPTY(*source)) {
|
||||
value = NULL;
|
||||
- result = value_fromwire(source, &value);
|
||||
+ result = value_fromwire(source, depth + 1, &value);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
isccc_sexpr_free(&list);
|
||||
return (result);
|
||||
@@ -699,7 +717,7 @@ isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
|
||||
if (version != 1)
|
||||
return (ISCCC_R_UNKNOWNVERSION);
|
||||
|
||||
- return (table_fromwire(source, secret, algorithm, alistp));
|
||||
+ return (table_fromwire(source, secret, algorithm, 0, alistp));
|
||||
}
|
||||
|
||||
static isc_result_t
|
||||
diff --git a/lib/isccc/include/isccc/result.h b/lib/isccc/include/isccc/result.h
|
||||
index 6c79dd7..a85861c 100644
|
||||
--- a/lib/isccc/include/isccc/result.h
|
||||
+++ b/lib/isccc/include/isccc/result.h
|
||||
@@ -47,8 +47,10 @@
|
||||
#define ISCCC_R_CLOCKSKEW (ISC_RESULTCLASS_ISCCC + 4)
|
||||
/*% Duplicate */
|
||||
#define ISCCC_R_DUPLICATE (ISC_RESULTCLASS_ISCCC + 5)
|
||||
+/*% Maximum recursion depth */
|
||||
+#define ISCCC_R_MAXDEPTH (ISC_RESULTCLASS_ISCCC + 6)
|
||||
|
||||
-#define ISCCC_R_NRESULTS 6 /*%< Number of results */
|
||||
+#define ISCCC_R_NRESULTS 7 /*%< Number of results */
|
||||
|
||||
ISC_LANG_BEGINDECLS
|
||||
|
||||
diff --git a/lib/isccc/result.c b/lib/isccc/result.c
|
||||
index 8419bbb..325200b 100644
|
||||
--- a/lib/isccc/result.c
|
||||
+++ b/lib/isccc/result.c
|
||||
@@ -40,7 +40,8 @@ static const char *text[ISCCC_R_NRESULTS] = {
|
||||
"bad auth", /* 3 */
|
||||
"expired", /* 4 */
|
||||
"clock skew", /* 5 */
|
||||
- "duplicate" /* 6 */
|
||||
+ "duplicate", /* 6 */
|
||||
+ "max depth", /* 7 */
|
||||
};
|
||||
|
||||
static const char *ids[ISCCC_R_NRESULTS] = {
|
||||
@@ -50,6 +51,7 @@ static const char *ids[ISCCC_R_NRESULTS] = {
|
||||
"ISCCC_R_EXPIRED",
|
||||
"ISCCC_R_CLOCKSKEW",
|
||||
"ISCCC_R_DUPLICATE",
|
||||
+ "ISCCC_R_MAXDEPTH",
|
||||
};
|
||||
|
||||
#define ISCCC_RESULT_RESULTSET 2
|
||||
--
|
||||
2.25.1
|
||||
|
||||
@@ -23,6 +23,7 @@ SRC_URI = "https://ftp.isc.org/isc/bind9/${PV}/${BPN}-${PV}.tar.gz \
|
||||
file://CVE-2022-38177.patch \
|
||||
file://CVE-2022-38178.patch \
|
||||
file://CVE-2023-2828.patch \
|
||||
file://CVE-2023-3341.patch \
|
||||
"
|
||||
|
||||
SRC_URI[sha256sum] = "0d8efbe7ec166ada90e46add4267b7e7c934790cba9bd5af6b8380a4fbfb5aff"
|
||||
|
||||
@@ -26,7 +26,7 @@ SRC_URI_append_class-nativesdk = " \
|
||||
file://environment.d-openssl.sh \
|
||||
"
|
||||
|
||||
SRC_URI[sha256sum] = "d6697e2871e77238460402e9362d47d18382b15ef9f246aba6c7bd780d38a6b0"
|
||||
SRC_URI[sha256sum] = "cf3098950cb4d853ad95c0841f1f9c6d3dc102dccfcacd521d93925208b76ac8"
|
||||
|
||||
inherit lib_package multilib_header multilib_script ptest
|
||||
MULTILIB_SCRIPTS = "${PN}-bin:${bindir}/c_rehash"
|
||||
@@ -11,7 +11,7 @@ AUTHOR = "Thomas Hood"
|
||||
HOMEPAGE = "http://packages.debian.org/resolvconf"
|
||||
RDEPENDS_${PN} = "bash"
|
||||
|
||||
SRC_URI = "git://salsa.debian.org/debian/resolvconf.git;protocol=https;branch=master \
|
||||
SRC_URI = "git://salsa.debian.org/debian/resolvconf.git;protocol=https;branch=unstable \
|
||||
file://fix-path-for-busybox.patch \
|
||||
file://99_resolvconf \
|
||||
"
|
||||
|
||||
82
meta/recipes-core/busybox/busybox/CVE-2022-48174.patch
Normal file
82
meta/recipes-core/busybox/busybox/CVE-2022-48174.patch
Normal file
@@ -0,0 +1,82 @@
|
||||
From c18ebf861528ef24958dd99a146482d2a40014c7 Mon Sep 17 00:00:00 2001
|
||||
From: Denys Vlasenko <vda.linux@googlemail.com>
|
||||
Date: Mon, 12 Jun 2023 17:48:47 +0200
|
||||
Subject: [PATCH] shell: avoid segfault on ${0::0/0~09J}. Closes 15216
|
||||
|
||||
function old new delta
|
||||
evaluate_string 1011 1053 +42
|
||||
|
||||
CVE: CVE-2022-48174
|
||||
Upstream-Status: Backport [d417193cf37ca1005830d7e16f5fa7e1d8a44209]
|
||||
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
|
||||
---
|
||||
shell/math.c | 39 +++++++++++++++++++++++++++++++++++----
|
||||
1 file changed, 35 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/shell/math.c b/shell/math.c
|
||||
index af1ab55c0..79824e81f 100644
|
||||
--- a/shell/math.c
|
||||
+++ b/shell/math.c
|
||||
@@ -578,6 +578,28 @@ static arith_t strto_arith_t(const char *nptr, char **endptr)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
+//TODO: much better estimation than expr_len/2? Such as:
|
||||
+//static unsigned estimate_nums_and_names(const char *expr)
|
||||
+//{
|
||||
+// unsigned count = 0;
|
||||
+// while (*(expr = skip_whitespace(expr)) != '\0') {
|
||||
+// const char *p;
|
||||
+// if (isdigit(*expr)) {
|
||||
+// while (isdigit(*++expr))
|
||||
+// continue;
|
||||
+// count++;
|
||||
+// continue;
|
||||
+// }
|
||||
+// p = endofname(expr);
|
||||
+// if (p != expr) {
|
||||
+// expr = p;
|
||||
+// count++;
|
||||
+// continue;
|
||||
+// }
|
||||
+// }
|
||||
+// return count;
|
||||
+//}
|
||||
+
|
||||
static arith_t FAST_FUNC
|
||||
evaluate_string(arith_state_t *math_state, const char *expr)
|
||||
{
|
||||
@@ -585,10 +607,12 @@ evaluate_string(arith_state_t *math_state, const char *expr)
|
||||
const char *errmsg;
|
||||
const char *start_expr = expr = skip_whitespace(expr);
|
||||
unsigned expr_len = strlen(expr) + 2;
|
||||
- /* Stack of integers */
|
||||
- /* The proof that there can be no more than strlen(startbuf)/2+1
|
||||
- * integers in any given correct or incorrect expression
|
||||
- * is left as an exercise to the reader. */
|
||||
+ /* Stack of integers/names */
|
||||
+ /* There can be no more than strlen(startbuf)/2+1
|
||||
+ * integers/names in any given correct or incorrect expression.
|
||||
+ * (modulo "09v09v09v09v09v" case,
|
||||
+ * but we have code to detect that early)
|
||||
+ */
|
||||
var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0]));
|
||||
var_or_num_t *numstackptr = numstack;
|
||||
/* Stack of operator tokens */
|
||||
@@ -657,6 +681,13 @@ evaluate_string(arith_state_t *math_state, const char *expr)
|
||||
numstackptr->var = NULL;
|
||||
errno = 0;
|
||||
numstackptr->val = strto_arith_t(expr, (char**) &expr);
|
||||
+ /* A number can't be followed by another number, or a variable name.
|
||||
+ * We'd catch this later anyway, but this would require numstack[]
|
||||
+ * to be twice as deep to handle strings where _every_ char is
|
||||
+ * a new number or name. Example: 09v09v09v09v09v09v09v09v09v
|
||||
+ */
|
||||
+ if (isalnum(*expr) || *expr == '_')
|
||||
+ goto err;
|
||||
if (errno)
|
||||
numstackptr->val = 0; /* bash compat */
|
||||
goto num;
|
||||
--
|
||||
2.40.1
|
||||
|
||||
@@ -55,6 +55,7 @@ SRC_URI = "https://busybox.net/downloads/busybox-${PV}.tar.bz2;name=tarball \
|
||||
file://CVE-2021-42374.patch \
|
||||
file://CVE-2021-42376.patch \
|
||||
file://CVE-2021-423xx-awk.patch \
|
||||
file://CVE-2022-48174.patch \
|
||||
file://0001-libbb-sockaddr2str-ensure-only-printable-characters-.patch \
|
||||
file://0002-nslookup-sanitize-all-printed-strings-with-printable.patch \
|
||||
"
|
||||
|
||||
@@ -8,6 +8,7 @@ SRC_URI = "https://dbus.freedesktop.org/releases/dbus/dbus-${PV}.tar.gz \
|
||||
file://tmpdir.patch \
|
||||
file://dbus-1.init \
|
||||
file://clear-guid_from_server-if-send_negotiate_unix_f.patch \
|
||||
file://CVE-2023-34969.patch \
|
||||
"
|
||||
|
||||
SRC_URI[sha256sum] = "bc42d196c1756ac520d61bf3ccd6f42013617def45dd1e591a6091abf51dca38"
|
||||
@@ -31,3 +32,5 @@ PACKAGECONFIG[systemd] = "--enable-systemd --with-systemdsystemunitdir=${systemd
|
||||
PACKAGECONFIG[x11] = "--with-x --enable-x11-autolaunch,--without-x --disable-x11-autolaunch, virtual/libx11 libsm"
|
||||
PACKAGECONFIG[user-session] = "--enable-user-session --with-systemduserunitdir=${systemd_user_unitdir},--disable-user-session"
|
||||
PACKAGECONFIG[verbose-mode] = "--enable-verbose-mode,,,"
|
||||
|
||||
CVE_PRODUCT += "d-bus_project:d-bus freedesktop:dbus freedesktop:libdbus"
|
||||
|
||||
96
meta/recipes-core/dbus/dbus/CVE-2023-34969.patch
Normal file
96
meta/recipes-core/dbus/dbus/CVE-2023-34969.patch
Normal file
@@ -0,0 +1,96 @@
|
||||
From 37a4dc5835731a1f7a81f1b67c45b8dfb556dd1c Mon Sep 17 00:00:00 2001
|
||||
From: hongjinghao <q1204531485@163.com>
|
||||
Date: Mon, 5 Jun 2023 18:17:06 +0100
|
||||
Subject: [PATCH] bus: Assign a serial number for messages from the driver
|
||||
|
||||
Normally, it's enough to rely on a message being given a serial number
|
||||
by the DBusConnection just before it is actually sent. However, in the
|
||||
rare case where the policy blocks the driver from sending a message
|
||||
(due to a deny rule or the outgoing message quota being full), we need
|
||||
to get a valid serial number sooner, so that we can copy it into the
|
||||
DBUS_HEADER_FIELD_REPLY_SERIAL field (which is mandatory) in the error
|
||||
message sent to monitors. Otherwise, the dbus-daemon will crash with
|
||||
an assertion failure if at least one Monitoring client is attached,
|
||||
because zero is not a valid serial number to copy.
|
||||
|
||||
This fixes a denial-of-service vulnerability: if a privileged user is
|
||||
monitoring the well-known system bus using a Monitoring client like
|
||||
dbus-monitor or `busctl monitor`, then an unprivileged user can cause
|
||||
denial-of-service by triggering this crash. A mitigation for this
|
||||
vulnerability is to avoid attaching Monitoring clients to the system
|
||||
bus when they are not needed. If there are no Monitoring clients, then
|
||||
the vulnerable code is not reached.
|
||||
|
||||
Co-authored-by: Simon McVittie <smcv@collabora.com>
|
||||
Resolves: dbus/dbus#457
|
||||
(cherry picked from commit b159849e031000d1dbc1ab876b5fc78a3ce9b534)
|
||||
---
|
||||
bus/connection.c | 15 +++++++++++++++
|
||||
dbus/dbus-connection-internal.h | 2 ++
|
||||
dbus/dbus-connection.c | 11 ++++++++++-
|
||||
3 files changed, 27 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/bus/connection.c b/bus/connection.c
|
||||
index b3583433..215f0230 100644
|
||||
--- a/bus/connection.c
|
||||
+++ b/bus/connection.c
|
||||
@@ -2350,6 +2350,21 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
|
||||
if (!dbus_message_set_sender (message, DBUS_SERVICE_DBUS))
|
||||
return FALSE;
|
||||
|
||||
+ /* Make sure the message has a non-zero serial number, otherwise
|
||||
+ * bus_transaction_capture_error_reply() will not be able to mock up
|
||||
+ * a corresponding reply for it. Normally this would be delayed until
|
||||
+ * the first time we actually send the message out from a
|
||||
+ * connection, when the transaction is committed, but that's too late
|
||||
+ * in this case.
|
||||
+ */
|
||||
+ if (dbus_message_get_serial (message) == 0)
|
||||
+ {
|
||||
+ dbus_uint32_t next_serial;
|
||||
+
|
||||
+ next_serial = _dbus_connection_get_next_client_serial (connection);
|
||||
+ dbus_message_set_serial (message, next_serial);
|
||||
+ }
|
||||
+
|
||||
if (bus_connection_is_active (connection))
|
||||
{
|
||||
if (!dbus_message_set_destination (message,
|
||||
diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h
|
||||
index 48357321..ba79b192 100644
|
||||
--- a/dbus/dbus-connection-internal.h
|
||||
+++ b/dbus/dbus-connection-internal.h
|
||||
@@ -54,6 +54,8 @@ DBUS_PRIVATE_EXPORT
|
||||
DBusConnection * _dbus_connection_ref_unlocked (DBusConnection *connection);
|
||||
DBUS_PRIVATE_EXPORT
|
||||
void _dbus_connection_unref_unlocked (DBusConnection *connection);
|
||||
+DBUS_PRIVATE_EXPORT
|
||||
+dbus_uint32_t _dbus_connection_get_next_client_serial (DBusConnection *connection);
|
||||
void _dbus_connection_queue_received_message_link (DBusConnection *connection,
|
||||
DBusList *link);
|
||||
dbus_bool_t _dbus_connection_has_messages_to_send_unlocked (DBusConnection *connection);
|
||||
diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
|
||||
index c525b6dc..09cef278 100644
|
||||
--- a/dbus/dbus-connection.c
|
||||
+++ b/dbus/dbus-connection.c
|
||||
@@ -1456,7 +1456,16 @@ _dbus_connection_unref_unlocked (DBusConnection *connection)
|
||||
_dbus_connection_last_unref (connection);
|
||||
}
|
||||
|
||||
-static dbus_uint32_t
|
||||
+/**
|
||||
+ * Allocate and return the next non-zero serial number for outgoing messages.
|
||||
+ *
|
||||
+ * This method is only valid to call from single-threaded code, such as
|
||||
+ * the dbus-daemon, or with the connection lock held.
|
||||
+ *
|
||||
+ * @param connection the connection
|
||||
+ * @returns A suitable serial number for the next message to be sent on the connection.
|
||||
+ */
|
||||
+dbus_uint32_t
|
||||
_dbus_connection_get_next_client_serial (DBusConnection *connection)
|
||||
{
|
||||
dbus_uint32_t serial;
|
||||
--
|
||||
2.25.1
|
||||
|
||||
290
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-29499.patch
Normal file
290
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-29499.patch
Normal file
@@ -0,0 +1,290 @@
|
||||
From 5f4485c4ff57fdefb1661531788def7ca5a47328 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Thu, 17 Aug 2023 04:19:44 +0000
|
||||
Subject: [PATCH] gvariant-serialiser: Check offset table entry size is minimal
|
||||
|
||||
The entries in an offset table (which is used for variable sized arrays
|
||||
and tuples containing variable sized members) are sized so that they can
|
||||
address every byte in the overall variant.
|
||||
|
||||
The specification requires that for a variant to be in normal form, its
|
||||
offset table entries must be the minimum width such that they can
|
||||
address every byte in the variant.
|
||||
|
||||
That minimality requirement was not checked in
|
||||
`g_variant_is_normal_form()`, leading to two different byte arrays being
|
||||
interpreted as the normal form of a given variant tree. That kind of
|
||||
confusion could potentially be exploited, and is certainly a bug.
|
||||
|
||||
Fix it by adding the necessary checks on offset table entry width, and
|
||||
unit tests.
|
||||
|
||||
Spotted by William Manley.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Fixes: #2794
|
||||
|
||||
CVE: CVE-2023-29499
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/5f4485c4ff57fdefb1661531788def7ca5a47328]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-serialiser.c | 19 +++-
|
||||
glib/tests/gvariant.c | 176 +++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 194 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index 0bf7243..5aa2cbc 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -694,6 +694,10 @@ gvs_variable_sized_array_get_frame_offsets (GVariantSerialised value)
|
||||
out.data_size = last_end;
|
||||
out.array = value.data + last_end;
|
||||
out.length = offsets_array_size / out.offset_size;
|
||||
+
|
||||
+ if (out.length > 0 && gvs_calculate_total_size (last_end, out.length) != value.size)
|
||||
+ return out; /* offset size not minimal */
|
||||
+
|
||||
out.is_normal = TRUE;
|
||||
|
||||
return out;
|
||||
@@ -1201,6 +1205,7 @@ gvs_tuple_is_normal (GVariantSerialised value)
|
||||
gsize length;
|
||||
gsize offset;
|
||||
gsize i;
|
||||
+ gsize offset_table_size;
|
||||
|
||||
/* as per the comment in gvs_tuple_get_child() */
|
||||
if G_UNLIKELY (value.data == NULL && value.size != 0)
|
||||
@@ -1305,7 +1310,19 @@ gvs_tuple_is_normal (GVariantSerialised value)
|
||||
}
|
||||
}
|
||||
|
||||
- return offset_ptr == offset;
|
||||
+ /* @offset_ptr has been counting backwards from the end of the variant, to
|
||||
+ * find the beginning of the offset table. @offset has been counting forwards
|
||||
+ * from the beginning of the variant to find the end of the data. They should
|
||||
+ * have met in the middle. */
|
||||
+ if (offset_ptr != offset)
|
||||
+ return FALSE;
|
||||
+
|
||||
+ offset_table_size = value.size - offset_ptr;
|
||||
+ if (value.size > 0 &&
|
||||
+ gvs_calculate_total_size (offset, offset_table_size / offset_size) != value.size)
|
||||
+ return FALSE; /* offset size not minimal */
|
||||
+
|
||||
+ return TRUE;
|
||||
}
|
||||
|
||||
/* Variants {{{2
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index d640c81..4ce0e4f 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -5092,6 +5092,86 @@ test_normal_checking_array_offsets2 (void)
|
||||
g_variant_unref (variant);
|
||||
}
|
||||
|
||||
+/* Test that an otherwise-valid serialised GVariant is considered non-normal if
|
||||
+ * its offset table entries are too wide.
|
||||
+ *
|
||||
+ * See §2.3.6 (Framing Offsets) of the GVariant specification. */
|
||||
+static void
|
||||
+test_normal_checking_array_offsets_minimal_sized (void)
|
||||
+{
|
||||
+ GVariantBuilder builder;
|
||||
+ gsize i;
|
||||
+ GVariant *aay_constructed = NULL;
|
||||
+ const guint8 *data = NULL;
|
||||
+ guint8 *data_owned = NULL;
|
||||
+ GVariant *aay_deserialised = NULL;
|
||||
+ GVariant *aay_normalised = NULL;
|
||||
+
|
||||
+ /* Construct an array of type aay, consisting of 128 elements which are each
|
||||
+ * an empty array, i.e. `[[] * 128]`. This is chosen because the inner
|
||||
+ * elements are variable sized (making the outer array variable sized, so it
|
||||
+ * must have an offset table), but they are also zero-sized when serialised.
|
||||
+ * So the serialised representation of @aay_constructed consists entirely of
|
||||
+ * its offset table, which is entirely zeroes.
|
||||
+ *
|
||||
+ * The array is chosen to be 128 elements long because that means offset
|
||||
+ * table entries which are 1 byte long. If the elements in the array were
|
||||
+ * non-zero-sized (to the extent that the overall array is ≥256 bytes long),
|
||||
+ * the offset table entries would end up being 2 bytes long. */
|
||||
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aay"));
|
||||
+
|
||||
+ for (i = 0; i < 128; i++)
|
||||
+ g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0));
|
||||
+
|
||||
+ aay_constructed = g_variant_builder_end (&builder);
|
||||
+
|
||||
+ /* Verify that the constructed array is in normal form, and its serialised
|
||||
+ * form is `b'\0' * 128`. */
|
||||
+ g_assert_true (g_variant_is_normal_form (aay_constructed));
|
||||
+ g_assert_cmpuint (g_variant_n_children (aay_constructed), ==, 128);
|
||||
+ g_assert_cmpuint (g_variant_get_size (aay_constructed), ==, 128);
|
||||
+
|
||||
+ data = g_variant_get_data (aay_constructed);
|
||||
+ for (i = 0; i < g_variant_get_size (aay_constructed); i++)
|
||||
+ g_assert_cmpuint (data[i], ==, 0);
|
||||
+
|
||||
+ /* Construct a serialised `aay` GVariant which is `b'\0' * 256`. This has to
|
||||
+ * be a non-normal form of `[[] * 128]`, with 2-byte-long offset table
|
||||
+ * entries, because each offset table entry has to be able to reference all of
|
||||
+ * the byte boundaries in the container. All the entries in the offset table
|
||||
+ * are zero, so all the elements of the array are zero-sized. */
|
||||
+ data = data_owned = g_malloc0 (256);
|
||||
+ aay_deserialised = g_variant_new_from_data (G_VARIANT_TYPE ("aay"),
|
||||
+ data,
|
||||
+ 256,
|
||||
+ FALSE,
|
||||
+ g_free,
|
||||
+ g_steal_pointer (&data_owned));
|
||||
+
|
||||
+ g_assert_false (g_variant_is_normal_form (aay_deserialised));
|
||||
+ g_assert_cmpuint (g_variant_n_children (aay_deserialised), ==, 128);
|
||||
+ g_assert_cmpuint (g_variant_get_size (aay_deserialised), ==, 256);
|
||||
+
|
||||
+ data = g_variant_get_data (aay_deserialised);
|
||||
+ for (i = 0; i < g_variant_get_size (aay_deserialised); i++)
|
||||
+ g_assert_cmpuint (data[i], ==, 0);
|
||||
+
|
||||
+ /* Get its normal form. That should change the serialised size. */
|
||||
+ aay_normalised = g_variant_get_normal_form (aay_deserialised);
|
||||
+
|
||||
+ g_assert_true (g_variant_is_normal_form (aay_normalised));
|
||||
+ g_assert_cmpuint (g_variant_n_children (aay_normalised), ==, 128);
|
||||
+ g_assert_cmpuint (g_variant_get_size (aay_normalised), ==, 128);
|
||||
+
|
||||
+ data = g_variant_get_data (aay_normalised);
|
||||
+ for (i = 0; i < g_variant_get_size (aay_normalised); i++)
|
||||
+ g_assert_cmpuint (data[i], ==, 0);
|
||||
+
|
||||
+ g_variant_unref (aay_normalised);
|
||||
+ g_variant_unref (aay_deserialised);
|
||||
+ g_variant_unref (aay_constructed);
|
||||
+}
|
||||
+
|
||||
/* Test that a tuple with invalidly large values in its offset table is
|
||||
* normalised successfully without looping infinitely. */
|
||||
static void
|
||||
@@ -5286,6 +5366,98 @@ test_normal_checking_tuple_offsets4 (void)
|
||||
g_variant_unref (variant);
|
||||
}
|
||||
|
||||
+/* Test that an otherwise-valid serialised GVariant is considered non-normal if
|
||||
+ * its offset table entries are too wide.
|
||||
+ *
|
||||
+ * See §2.3.6 (Framing Offsets) of the GVariant specification. */
|
||||
+static void
|
||||
+test_normal_checking_tuple_offsets_minimal_sized (void)
|
||||
+{
|
||||
+ GString *type_string = NULL;
|
||||
+ GVariantBuilder builder;
|
||||
+ gsize i;
|
||||
+ GVariant *ray_constructed = NULL;
|
||||
+ const guint8 *data = NULL;
|
||||
+ guint8 *data_owned = NULL;
|
||||
+ GVariant *ray_deserialised = NULL;
|
||||
+ GVariant *ray_normalised = NULL;
|
||||
+
|
||||
+ /* Construct a tuple of type (ay…ay), consisting of 129 members which are each
|
||||
+ * an empty array, i.e. `([] * 129)`. This is chosen because the inner
|
||||
+ * members are variable sized, so the outer tuple must have an offset table,
|
||||
+ * but they are also zero-sized when serialised. So the serialised
|
||||
+ * representation of @ray_constructed consists entirely of its offset table,
|
||||
+ * which is entirely zeroes.
|
||||
+ *
|
||||
+ * The tuple is chosen to be 129 members long because that means it has 128
|
||||
+ * offset table entries which are 1 byte long each. If the members in the
|
||||
+ * tuple were non-zero-sized (to the extent that the overall tuple is ≥256
|
||||
+ * bytes long), the offset table entries would end up being 2 bytes long.
|
||||
+ *
|
||||
+ * 129 members are used unlike 128 array elements in
|
||||
+ * test_normal_checking_array_offsets_minimal_sized(), because the last member
|
||||
+ * in a tuple never needs an offset table entry. */
|
||||
+ type_string = g_string_new ("");
|
||||
+ g_string_append_c (type_string, '(');
|
||||
+ for (i = 0; i < 129; i++)
|
||||
+ g_string_append (type_string, "ay");
|
||||
+ g_string_append_c (type_string, ')');
|
||||
+
|
||||
+ g_variant_builder_init (&builder, G_VARIANT_TYPE (type_string->str));
|
||||
+
|
||||
+ for (i = 0; i < 129; i++)
|
||||
+ g_variant_builder_add_value (&builder, g_variant_new_array (G_VARIANT_TYPE_BYTE, NULL, 0));
|
||||
+
|
||||
+ ray_constructed = g_variant_builder_end (&builder);
|
||||
+
|
||||
+ /* Verify that the constructed tuple is in normal form, and its serialised
|
||||
+ * form is `b'\0' * 128`. */
|
||||
+ g_assert_true (g_variant_is_normal_form (ray_constructed));
|
||||
+ g_assert_cmpuint (g_variant_n_children (ray_constructed), ==, 129);
|
||||
+ g_assert_cmpuint (g_variant_get_size (ray_constructed), ==, 128);
|
||||
+
|
||||
+ data = g_variant_get_data (ray_constructed);
|
||||
+ for (i = 0; i < g_variant_get_size (ray_constructed); i++)
|
||||
+ g_assert_cmpuint (data[i], ==, 0);
|
||||
+
|
||||
+ /* Construct a serialised `(ay…ay)` GVariant which is `b'\0' * 256`. This has
|
||||
+ * to be a non-normal form of `([] * 129)`, with 2-byte-long offset table
|
||||
+ * entries, because each offset table entry has to be able to reference all of
|
||||
+ * the byte boundaries in the container. All the entries in the offset table
|
||||
+ * are zero, so all the members of the tuple are zero-sized. */
|
||||
+ data = data_owned = g_malloc0 (256);
|
||||
+ ray_deserialised = g_variant_new_from_data (G_VARIANT_TYPE (type_string->str),
|
||||
+ data,
|
||||
+ 256,
|
||||
+ FALSE,
|
||||
+ g_free,
|
||||
+ g_steal_pointer (&data_owned));
|
||||
+
|
||||
+ g_assert_false (g_variant_is_normal_form (ray_deserialised));
|
||||
+ g_assert_cmpuint (g_variant_n_children (ray_deserialised), ==, 129);
|
||||
+ g_assert_cmpuint (g_variant_get_size (ray_deserialised), ==, 256);
|
||||
+
|
||||
+ data = g_variant_get_data (ray_deserialised);
|
||||
+ for (i = 0; i < g_variant_get_size (ray_deserialised); i++)
|
||||
+ g_assert_cmpuint (data[i], ==, 0);
|
||||
+
|
||||
+ /* Get its normal form. That should change the serialised size. */
|
||||
+ ray_normalised = g_variant_get_normal_form (ray_deserialised);
|
||||
+
|
||||
+ g_assert_true (g_variant_is_normal_form (ray_normalised));
|
||||
+ g_assert_cmpuint (g_variant_n_children (ray_normalised), ==, 129);
|
||||
+ g_assert_cmpuint (g_variant_get_size (ray_normalised), ==, 128);
|
||||
+
|
||||
+ data = g_variant_get_data (ray_normalised);
|
||||
+ for (i = 0; i < g_variant_get_size (ray_normalised); i++)
|
||||
+ g_assert_cmpuint (data[i], ==, 0);
|
||||
+
|
||||
+ g_variant_unref (ray_normalised);
|
||||
+ g_variant_unref (ray_deserialised);
|
||||
+ g_variant_unref (ray_constructed);
|
||||
+ g_string_free (type_string, TRUE);
|
||||
+}
|
||||
+
|
||||
/* Test that an empty object path is normalised successfully to the base object
|
||||
* path, ‘/’. */
|
||||
static void
|
||||
@@ -5431,6 +5603,8 @@ main (int argc, char **argv)
|
||||
test_normal_checking_array_offsets);
|
||||
g_test_add_func ("/gvariant/normal-checking/array-offsets2",
|
||||
test_normal_checking_array_offsets2);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/array-offsets/minimal-sized",
|
||||
+ test_normal_checking_array_offsets_minimal_sized);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
|
||||
test_normal_checking_tuple_offsets);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets2",
|
||||
@@ -5439,6 +5613,8 @@ main (int argc, char **argv)
|
||||
test_normal_checking_tuple_offsets3);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets4",
|
||||
test_normal_checking_tuple_offsets4);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized",
|
||||
+ test_normal_checking_tuple_offsets_minimal_sized);
|
||||
g_test_add_func ("/gvariant/normal-checking/empty-object-path",
|
||||
test_normal_checking_empty_object_path);
|
||||
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
From 1deacdd4e8e35a5cf1417918ca4f6b0afa6409b1 Mon Sep 17 00:00:00 2001
|
||||
From: William Manley <will@stb-tester.com>
|
||||
Date: Wed, 9 Aug 2023 10:04:49 +0000
|
||||
Subject: [PATCH] gvariant-core: Consolidate construction of
|
||||
`GVariantSerialised`
|
||||
|
||||
So I only need to change it in one place.
|
||||
|
||||
This introduces no functional changes.
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/1deacdd4e8e35a5cf1417918ca4f6b0afa6409b1]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant.c | 8 +++++---
|
||||
glib/tests/gvariant.c | 24 ++++++++++++++++++++++++
|
||||
2 files changed, 29 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||||
index 8ba701e..4dbd9e8 100644
|
||||
--- a/glib/gvariant.c
|
||||
+++ b/glib/gvariant.c
|
||||
@@ -5952,14 +5952,16 @@ g_variant_byteswap (GVariant *value)
|
||||
g_variant_serialised_byteswap (serialised);
|
||||
|
||||
bytes = g_bytes_new_take (serialised.data, serialised.size);
|
||||
- new = g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE);
|
||||
+ new = g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE));
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
else
|
||||
/* contains no multi-byte data */
|
||||
- new = value;
|
||||
+ new = g_variant_get_normal_form (value);
|
||||
|
||||
- return g_variant_ref_sink (new);
|
||||
+ g_assert (g_variant_is_trusted (new));
|
||||
+
|
||||
+ return g_steal_pointer (&new);
|
||||
}
|
||||
|
||||
/**
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index 4ce0e4f..3dda08e 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -3834,6 +3834,29 @@ test_gv_byteswap (void)
|
||||
g_free (string);
|
||||
}
|
||||
|
||||
+static void
|
||||
+test_gv_byteswap_non_normal_non_aligned (void)
|
||||
+{
|
||||
+ const guint8 data[] = { 0x02 };
|
||||
+ GVariant *v = NULL;
|
||||
+ GVariant *v_byteswapped = NULL;
|
||||
+
|
||||
+ g_test_summary ("Test that calling g_variant_byteswap() on a variant which "
|
||||
+ "is in non-normal form and doesn’t need byteswapping returns "
|
||||
+ "the same variant in normal form.");
|
||||
+
|
||||
+ v = g_variant_new_from_data (G_VARIANT_TYPE_BOOLEAN, data, sizeof (data), FALSE, NULL, NULL);
|
||||
+ g_assert_false (g_variant_is_normal_form (v));
|
||||
+
|
||||
+ v_byteswapped = g_variant_byteswap (v);
|
||||
+ g_assert_true (g_variant_is_normal_form (v_byteswapped));
|
||||
+
|
||||
+ g_assert_cmpvariant (v, v_byteswapped);
|
||||
+
|
||||
+ g_variant_unref (v);
|
||||
+ g_variant_unref (v_byteswapped);
|
||||
+}
|
||||
+
|
||||
static void
|
||||
test_parser (void)
|
||||
{
|
||||
@@ -5570,6 +5593,7 @@ main (int argc, char **argv)
|
||||
g_test_add_func ("/gvariant/builder-memory", test_builder_memory);
|
||||
g_test_add_func ("/gvariant/hashing", test_hashing);
|
||||
g_test_add_func ("/gvariant/byteswap", test_gv_byteswap);
|
||||
+ g_test_add_func ("/gvariant/byteswap/non-normal-non-aligned", test_gv_byteswap_non_normal_non_aligned);
|
||||
g_test_add_func ("/gvariant/parser", test_parses);
|
||||
g_test_add_func ("/gvariant/parser/integer-bounds", test_parser_integer_bounds);
|
||||
g_test_add_func ("/gvariant/parser/recursion", test_parser_recursion);
|
||||
--
|
||||
2.24.4
|
||||
|
||||
255
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32611-0002.patch
Normal file
255
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32611-0002.patch
Normal file
@@ -0,0 +1,255 @@
|
||||
From 446e69f5edd72deb2196dee36bbaf8056caf6948 Mon Sep 17 00:00:00 2001
|
||||
From: William Manley <will@stb-tester.com>
|
||||
Date: Wed, 9 Aug 2023 10:39:34 +0000
|
||||
Subject: [PATCH] gvariant-serialiser: Factor out functions for dealing with
|
||||
framing offsets
|
||||
|
||||
This introduces no functional changes.
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/446e69f5edd72deb2196dee36bbaf8056caf6948]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant.c | 81 +++++++++++++++++++++++++++++++++----------
|
||||
glib/tests/gvariant.c | 57 ++++++++++++++++++++++++++----
|
||||
2 files changed, 112 insertions(+), 26 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||||
index 4dbd9e8..a80c2c9 100644
|
||||
--- a/glib/gvariant.c
|
||||
+++ b/glib/gvariant.c
|
||||
@@ -5788,7 +5788,8 @@ g_variant_iter_loop (GVariantIter *iter,
|
||||
|
||||
/* Serialised data {{{1 */
|
||||
static GVariant *
|
||||
-g_variant_deep_copy (GVariant *value)
|
||||
+g_variant_deep_copy (GVariant *value,
|
||||
+ gboolean byteswap)
|
||||
{
|
||||
switch (g_variant_classify (value))
|
||||
{
|
||||
@@ -5806,7 +5807,7 @@ g_variant_deep_copy (GVariant *value)
|
||||
for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++)
|
||||
{
|
||||
GVariant *child = g_variant_get_child_value (value, i);
|
||||
- g_variant_builder_add_value (&builder, g_variant_deep_copy (child));
|
||||
+ g_variant_builder_add_value (&builder, g_variant_deep_copy (child, byteswap));
|
||||
g_variant_unref (child);
|
||||
}
|
||||
|
||||
@@ -5820,28 +5821,63 @@ g_variant_deep_copy (GVariant *value)
|
||||
return g_variant_new_byte (g_variant_get_byte (value));
|
||||
|
||||
case G_VARIANT_CLASS_INT16:
|
||||
- return g_variant_new_int16 (g_variant_get_int16 (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_int16 (GUINT16_SWAP_LE_BE (g_variant_get_int16 (value)));
|
||||
+ else
|
||||
+ return g_variant_new_int16 (g_variant_get_int16 (value));
|
||||
|
||||
case G_VARIANT_CLASS_UINT16:
|
||||
- return g_variant_new_uint16 (g_variant_get_uint16 (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_uint16 (GUINT16_SWAP_LE_BE (g_variant_get_uint16 (value)));
|
||||
+ else
|
||||
+ return g_variant_new_uint16 (g_variant_get_uint16 (value));
|
||||
|
||||
case G_VARIANT_CLASS_INT32:
|
||||
- return g_variant_new_int32 (g_variant_get_int32 (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_int32 (GUINT32_SWAP_LE_BE (g_variant_get_int32 (value)));
|
||||
+ else
|
||||
+ return g_variant_new_int32 (g_variant_get_int32 (value));
|
||||
|
||||
case G_VARIANT_CLASS_UINT32:
|
||||
- return g_variant_new_uint32 (g_variant_get_uint32 (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_uint32 (GUINT32_SWAP_LE_BE (g_variant_get_uint32 (value)));
|
||||
+ else
|
||||
+ return g_variant_new_uint32 (g_variant_get_uint32 (value));
|
||||
|
||||
case G_VARIANT_CLASS_INT64:
|
||||
- return g_variant_new_int64 (g_variant_get_int64 (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_int64 (GUINT64_SWAP_LE_BE (g_variant_get_int64 (value)));
|
||||
+ else
|
||||
+ return g_variant_new_int64 (g_variant_get_int64 (value));
|
||||
|
||||
case G_VARIANT_CLASS_UINT64:
|
||||
- return g_variant_new_uint64 (g_variant_get_uint64 (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_uint64 (GUINT64_SWAP_LE_BE (g_variant_get_uint64 (value)));
|
||||
+ else
|
||||
+ return g_variant_new_uint64 (g_variant_get_uint64 (value));
|
||||
|
||||
case G_VARIANT_CLASS_HANDLE:
|
||||
- return g_variant_new_handle (g_variant_get_handle (value));
|
||||
+ if (byteswap)
|
||||
+ return g_variant_new_handle (GUINT32_SWAP_LE_BE (g_variant_get_handle (value)));
|
||||
+ else
|
||||
+ return g_variant_new_handle (g_variant_get_handle (value));
|
||||
|
||||
case G_VARIANT_CLASS_DOUBLE:
|
||||
- return g_variant_new_double (g_variant_get_double (value));
|
||||
+ if (byteswap)
|
||||
+ {
|
||||
+ /* We have to convert the double to a uint64 here using a union,
|
||||
+ * because a cast will round it numerically. */
|
||||
+ union
|
||||
+ {
|
||||
+ guint64 u64;
|
||||
+ gdouble dbl;
|
||||
+ } u1, u2;
|
||||
+ u1.dbl = g_variant_get_double (value);
|
||||
+ u2.u64 = GUINT64_SWAP_LE_BE (u1.u64);
|
||||
+ return g_variant_new_double (u2.dbl);
|
||||
+ }
|
||||
+ else
|
||||
+ return g_variant_new_double (g_variant_get_double (value));
|
||||
|
||||
case G_VARIANT_CLASS_STRING:
|
||||
return g_variant_new_string (g_variant_get_string (value, NULL));
|
||||
@@ -5896,7 +5932,7 @@ g_variant_get_normal_form (GVariant *value)
|
||||
if (g_variant_is_normal_form (value))
|
||||
return g_variant_ref (value);
|
||||
|
||||
- trusted = g_variant_deep_copy (value);
|
||||
+ trusted = g_variant_deep_copy (value, FALSE);
|
||||
g_assert (g_variant_is_trusted (trusted));
|
||||
|
||||
return g_variant_ref_sink (trusted);
|
||||
@@ -5916,6 +5952,11 @@ g_variant_get_normal_form (GVariant *value)
|
||||
* contain multi-byte numeric data. That include strings, booleans,
|
||||
* bytes and containers containing only these things (recursively).
|
||||
*
|
||||
+ * While this function can safely handle untrusted, non-normal data, it is
|
||||
+ * recommended to check whether the input is in normal form beforehand, using
|
||||
+ * g_variant_is_normal_form(), and to reject non-normal inputs if your
|
||||
+ * application can be strict about what inputs it rejects.
|
||||
+ *
|
||||
* The returned value is always in normal form and is marked as trusted.
|
||||
*
|
||||
* Returns: (transfer full): the byteswapped form of @value
|
||||
@@ -5933,21 +5974,20 @@ g_variant_byteswap (GVariant *value)
|
||||
|
||||
g_variant_type_info_query (type_info, &alignment, NULL);
|
||||
|
||||
- if (alignment)
|
||||
- /* (potentially) contains multi-byte numeric data */
|
||||
+ if (alignment && g_variant_is_normal_form (value))
|
||||
{
|
||||
+ /* (potentially) contains multi-byte numeric data, but is also already in
|
||||
+ * normal form so we can use a faster byteswapping codepath on the
|
||||
+ * serialised data */
|
||||
GVariantSerialised serialised = { 0, };
|
||||
- GVariant *trusted;
|
||||
GBytes *bytes;
|
||||
|
||||
- trusted = g_variant_get_normal_form (value);
|
||||
- serialised.type_info = g_variant_get_type_info (trusted);
|
||||
- serialised.size = g_variant_get_size (trusted);
|
||||
+ serialised.type_info = g_variant_get_type_info (value);
|
||||
+ serialised.size = g_variant_get_size (value);
|
||||
serialised.data = g_malloc (serialised.size);
|
||||
serialised.ordered_offsets_up_to = G_MAXSIZE; /* operating on the normal form */
|
||||
serialised.checked_offsets_up_to = G_MAXSIZE;
|
||||
- g_variant_store (trusted, serialised.data);
|
||||
- g_variant_unref (trusted);
|
||||
+ g_variant_store (value, serialised.data);
|
||||
|
||||
g_variant_serialised_byteswap (serialised);
|
||||
|
||||
@@ -5955,6 +5995,9 @@ g_variant_byteswap (GVariant *value)
|
||||
new = g_variant_ref_sink (g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE));
|
||||
g_bytes_unref (bytes);
|
||||
}
|
||||
+ else if (alignment)
|
||||
+ /* (potentially) contains multi-byte numeric data */
|
||||
+ new = g_variant_ref_sink (g_variant_deep_copy (value, TRUE));
|
||||
else
|
||||
/* contains no multi-byte data */
|
||||
new = g_variant_get_normal_form (value);
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index 3dda08e..679dd40 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -2284,24 +2284,67 @@ serialise_tree (TreeInstance *tree,
|
||||
static void
|
||||
test_byteswap (void)
|
||||
{
|
||||
- GVariantSerialised one = { 0, }, two = { 0, };
|
||||
+ GVariantSerialised one = { 0, }, two = { 0, }, three = { 0, };
|
||||
TreeInstance *tree;
|
||||
-
|
||||
+ GVariant *one_variant = NULL;
|
||||
+ GVariant *two_variant = NULL;
|
||||
+ GVariant *two_byteswapped = NULL;
|
||||
+ GVariant *three_variant = NULL;
|
||||
+ GVariant *three_byteswapped = NULL;
|
||||
+ guint8 *three_data_copy = NULL;
|
||||
+ gsize three_size_copy = 0;
|
||||
+
|
||||
+ /* Write a tree out twice, once normally and once byteswapped. */
|
||||
tree = tree_instance_new (NULL, 3);
|
||||
serialise_tree (tree, &one);
|
||||
|
||||
+ one_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (one.type_info)),
|
||||
+ one.data, one.size, FALSE, NULL, NULL);
|
||||
+
|
||||
i_am_writing_byteswapped = TRUE;
|
||||
serialise_tree (tree, &two);
|
||||
+ serialise_tree (tree, &three);
|
||||
i_am_writing_byteswapped = FALSE;
|
||||
|
||||
- g_variant_serialised_byteswap (two);
|
||||
-
|
||||
- g_assert_cmpmem (one.data, one.size, two.data, two.size);
|
||||
- g_assert_cmpuint (one.depth, ==, two.depth);
|
||||
-
|
||||
+ /* Swap the first byteswapped one back using the function we want to test. */
|
||||
+ two_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (two.type_info)),
|
||||
+ two.data, two.size, FALSE, NULL, NULL);
|
||||
+ two_byteswapped = g_variant_byteswap (two_variant);
|
||||
+
|
||||
+ /* Make the second byteswapped one non-normal (hopefully), and then byteswap
|
||||
+ * it back using the function we want to test in its non-normal mode.
|
||||
+ * This might not work because it’s not necessarily possible to make an
|
||||
+ * arbitrary random variant non-normal. Adding a single zero byte to the end
|
||||
+ * often makes something non-normal but still readable. */
|
||||
+ three_size_copy = three.size + 1;
|
||||
+ three_data_copy = g_malloc (three_size_copy);
|
||||
+ memcpy (three_data_copy, three.data, three.size);
|
||||
+ three_data_copy[three.size] = '\0';
|
||||
+
|
||||
+ three_variant = g_variant_new_from_data (G_VARIANT_TYPE (g_variant_type_info_get_type_string (three.type_info)),
|
||||
+ three_data_copy, three_size_copy, FALSE, NULL, NULL);
|
||||
+ three_byteswapped = g_variant_byteswap (three_variant);
|
||||
+
|
||||
+ /* Check they’re the same. We can always compare @one_variant and
|
||||
+ * @two_byteswapped. We can only compare @two_byteswapped and
|
||||
+ * @three_byteswapped if @two_variant and @three_variant are equal: in that
|
||||
+ * case, the corruption to @three_variant was enough to make it non-normal but
|
||||
+ * not enough to change its value. */
|
||||
+ g_assert_cmpvariant (one_variant, two_byteswapped);
|
||||
+
|
||||
+ if (g_variant_equal (two_variant, three_variant))
|
||||
+ g_assert_cmpvariant (two_byteswapped, three_byteswapped);
|
||||
+
|
||||
+ g_variant_unref (three_byteswapped);
|
||||
+ g_variant_unref (three_variant);
|
||||
+ g_variant_unref (two_byteswapped);
|
||||
+ g_variant_unref (two_variant);
|
||||
+ g_variant_unref (one_variant);
|
||||
tree_instance_free (tree);
|
||||
g_free (one.data);
|
||||
g_free (two.data);
|
||||
+ g_free (three.data);
|
||||
+ g_free (three_data_copy);
|
||||
}
|
||||
|
||||
static void
|
||||
--
|
||||
2.24.4
|
||||
|
||||
49
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32636.patch
Normal file
49
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32636.patch
Normal file
@@ -0,0 +1,49 @@
|
||||
From 21a204147b16539b3eda3143b32844c49e29f4d4 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Thu, 17 Aug 2023 11:33:49 +0000
|
||||
Subject: [PATCH] gvariant: Propagate trust when getting a child of a
|
||||
serialised variant
|
||||
|
||||
If a variant is trusted, that means all its children are trusted, so
|
||||
ensure that their checked offsets are set as such.
|
||||
|
||||
This allows a lot of the offset table checks to be avoided when getting
|
||||
children from trusted serialised tuples, which speeds things up.
|
||||
|
||||
No unit test is included because this is just a performance fix. If
|
||||
there are other slownesses, or regressions, in serialised `GVariant`
|
||||
performance, the fuzzing setup will catch them like it did this one.
|
||||
|
||||
This change does reduce the time to run the oss-fuzz reproducer from 80s
|
||||
to about 0.7s on my machine.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Fixes: #2841
|
||||
oss-fuzz#54314
|
||||
|
||||
CVE: CVE-2023-32636
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/21a204147b16539b3eda3143b32844c49e29f4d4]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-core.c | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
|
||||
index 1b9d5cc..ed57c70 100644
|
||||
--- a/glib/gvariant-core.c
|
||||
+++ b/glib/gvariant-core.c
|
||||
@@ -1173,8 +1173,8 @@ g_variant_get_child_value (GVariant *value,
|
||||
child->contents.serialised.bytes =
|
||||
g_bytes_ref (value->contents.serialised.bytes);
|
||||
child->contents.serialised.data = s_child.data;
|
||||
- child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to;
|
||||
- child->contents.serialised.checked_offsets_up_to = s_child.checked_offsets_up_to;
|
||||
+ child->contents.serialised.ordered_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.ordered_offsets_up_to;
|
||||
+ child->contents.serialised.checked_offsets_up_to = (value->state & STATE_TRUSTED) ? G_MAXSIZE : s_child.checked_offsets_up_to;
|
||||
|
||||
return child;
|
||||
}
|
||||
--
|
||||
2.24.4
|
||||
|
||||
154
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32643.patch
Normal file
154
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32643.patch
Normal file
@@ -0,0 +1,154 @@
|
||||
From 78da5faccb3e065116b75b3ff87ff55381da6c76 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Thu, 17 Aug 2023 11:24:43 +0000
|
||||
Subject: [PATCH] gvariant: Check offset table doesn't fall outside variant
|
||||
bounds
|
||||
|
||||
When dereferencing the first entry in the offset table for a tuple,
|
||||
check that it doesn’t fall outside the bounds of the variant first.
|
||||
|
||||
This prevents an out-of-bounds read from some non-normal tuples.
|
||||
|
||||
This bug was introduced in commit 73d0aa81c2575a5c9ae77d.
|
||||
|
||||
Includes a unit test, although the test will likely only catch the
|
||||
original bug if run with asan enabled.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Fixes: #2840
|
||||
oss-fuzz#54302
|
||||
|
||||
CVE: CVE-2023-32643
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/78da5faccb3e065116b75b3ff87ff55381da6c76]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-serialiser.c | 12 ++++++--
|
||||
glib/tests/gvariant.c | 63 ++++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 72 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index 5aa2cbc..4e50ed7 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -979,7 +979,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised value,
|
||||
|
||||
member_info = g_variant_type_info_member_info (value.type_info, index_);
|
||||
|
||||
- if (member_info->i + 1)
|
||||
+ if (member_info->i + 1 &&
|
||||
+ offset_size * (member_info->i + 1) <= value.size)
|
||||
member_start = gvs_read_unaligned_le (value.data + value.size -
|
||||
offset_size * (member_info->i + 1),
|
||||
offset_size);
|
||||
@@ -990,7 +991,8 @@ gvs_tuple_get_member_bounds (GVariantSerialised value,
|
||||
member_start &= member_info->b;
|
||||
member_start |= member_info->c;
|
||||
|
||||
- if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST)
|
||||
+ if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST &&
|
||||
+ offset_size * (member_info->i + 1) <= value.size)
|
||||
member_end = value.size - offset_size * (member_info->i + 1);
|
||||
|
||||
else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
|
||||
@@ -1001,11 +1003,15 @@ gvs_tuple_get_member_bounds (GVariantSerialised value,
|
||||
member_end = member_start + fixed_size;
|
||||
}
|
||||
|
||||
- else /* G_VARIANT_MEMBER_ENDING_OFFSET */
|
||||
+ else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET &&
|
||||
+ offset_size * (member_info->i + 2) <= value.size)
|
||||
member_end = gvs_read_unaligned_le (value.data + value.size -
|
||||
offset_size * (member_info->i + 2),
|
||||
offset_size);
|
||||
|
||||
+ else /* invalid */
|
||||
+ member_end = G_MAXSIZE;
|
||||
+
|
||||
if (out_member_start != NULL)
|
||||
*out_member_start = member_start;
|
||||
if (out_member_end != NULL)
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index 679dd40..2eca8be 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -5432,6 +5432,67 @@ test_normal_checking_tuple_offsets4 (void)
|
||||
g_variant_unref (variant);
|
||||
}
|
||||
|
||||
+/* This is a regression test that dereferencing the first element in the offset
|
||||
+ * table doesn’t dereference memory before the start of the GVariant. The first
|
||||
+ * element in the offset table gives the offset of the final member in the
|
||||
+ * tuple (the offset table is stored in reverse), and the position of this final
|
||||
+ * member is needed to check that none of the tuple members overlap with the
|
||||
+ * offset table
|
||||
+ *
|
||||
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2840 */
|
||||
+static void
|
||||
+test_normal_checking_tuple_offsets5 (void)
|
||||
+{
|
||||
+ /* A tuple of type (sss) in normal form would have an offset table with two
|
||||
+ * entries:
|
||||
+ * - The first entry (lowest index in the table) gives the offset of the
|
||||
+ * third `s` in the tuple, as the offset table is reversed compared to the
|
||||
+ * tuple members.
|
||||
+ * - The second entry (highest index in the table) gives the offset of the
|
||||
+ * second `s` in the tuple.
|
||||
+ * - The offset of the first `s` in the tuple is always 0.
|
||||
+ *
|
||||
+ * See §2.5.4 (Structures) of the GVariant specification for details, noting
|
||||
+ * that the table is only layed out this way because all three members of the
|
||||
+ * tuple have non-fixed sizes.
|
||||
+ *
|
||||
+ * It’s not clear whether the 0xaa data of this variant is part of the strings
|
||||
+ * in the tuple, or part of the offset table. It doesn’t really matter. This
|
||||
+ * is a regression test to check that the code to validate the offset table
|
||||
+ * doesn’t unconditionally try to access the first entry in the offset table
|
||||
+ * by subtracting the table size from the end of the GVariant data.
|
||||
+ *
|
||||
+ * In this non-normal case, that would result in an address off the start of
|
||||
+ * the GVariant data, and an out-of-bounds read, because the GVariant is one
|
||||
+ * byte long, but the offset table is calculated as two bytes long (with 1B
|
||||
+ * sized entries) from the tuple’s type.
|
||||
+ */
|
||||
+ const GVariantType *data_type = G_VARIANT_TYPE ("(sss)");
|
||||
+ const guint8 data[] = { 0xaa };
|
||||
+ gsize size = sizeof (data);
|
||||
+ GVariant *variant = NULL;
|
||||
+ GVariant *normal_variant = NULL;
|
||||
+ GVariant *expected = NULL;
|
||||
+
|
||||
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/2840");
|
||||
+
|
||||
+ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
|
||||
+ g_assert_nonnull (variant);
|
||||
+
|
||||
+ g_assert_false (g_variant_is_normal_form (variant));
|
||||
+
|
||||
+ normal_variant = g_variant_get_normal_form (variant);
|
||||
+ g_assert_nonnull (normal_variant);
|
||||
+
|
||||
+ expected = g_variant_new_parsed ("('', '', '')");
|
||||
+ g_assert_cmpvariant (expected, variant);
|
||||
+ g_assert_cmpvariant (expected, normal_variant);
|
||||
+
|
||||
+ g_variant_unref (expected);
|
||||
+ g_variant_unref (normal_variant);
|
||||
+ g_variant_unref (variant);
|
||||
+}
|
||||
+
|
||||
/* Test that an otherwise-valid serialised GVariant is considered non-normal if
|
||||
* its offset table entries are too wide.
|
||||
*
|
||||
@@ -5680,6 +5741,8 @@ main (int argc, char **argv)
|
||||
test_normal_checking_tuple_offsets3);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets4",
|
||||
test_normal_checking_tuple_offsets4);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets5",
|
||||
+ test_normal_checking_tuple_offsets5);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets/minimal-sized",
|
||||
test_normal_checking_tuple_offsets_minimal_sized);
|
||||
g_test_add_func ("/gvariant/normal-checking/empty-object-path",
|
||||
--
|
||||
2.24.4
|
||||
|
||||
103
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0001.patch
Normal file
103
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0001.patch
Normal file
@@ -0,0 +1,103 @@
|
||||
From 1deacdd4e8e35a5cf1417918ca4f6b0afa6409b1 Mon Sep 17 00:00:00 2001
|
||||
From: William Manley <will@stb-tester.com>
|
||||
Date: Wed, 9 Aug 2023 10:04:49 +0000
|
||||
Subject: [PATCH] gvariant-core: Consolidate construction of
|
||||
`GVariantSerialised`
|
||||
|
||||
So I only need to change it in one place.
|
||||
|
||||
This introduces no functional changes.
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/1deacdd4e8e35a5cf1417918ca4f6b0afa6409b1]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-core.c | 49 ++++++++++++++++++++++----------------------
|
||||
1 file changed, 25 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
|
||||
index 9397573..aa0e0a0 100644
|
||||
--- a/glib/gvariant-core.c
|
||||
+++ b/glib/gvariant-core.c
|
||||
@@ -349,6 +349,27 @@ g_variant_ensure_size (GVariant *value)
|
||||
}
|
||||
}
|
||||
|
||||
+/* < private >
|
||||
+ * g_variant_to_serialised:
|
||||
+ * @value: a #GVariant
|
||||
+ *
|
||||
+ * Gets a GVariantSerialised for a GVariant in state STATE_SERIALISED.
|
||||
+ */
|
||||
+inline static GVariantSerialised
|
||||
+g_variant_to_serialised (GVariant *value)
|
||||
+{
|
||||
+ g_assert (value->state & STATE_SERIALISED);
|
||||
+ {
|
||||
+ GVariantSerialised serialised = {
|
||||
+ value->type_info,
|
||||
+ (gpointer) value->contents.serialised.data,
|
||||
+ value->size,
|
||||
+ value->depth,
|
||||
+ };
|
||||
+ return serialised;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/* < private >
|
||||
* g_variant_serialise:
|
||||
* @value: a #GVariant
|
||||
@@ -991,16 +1012,8 @@ g_variant_n_children (GVariant *value)
|
||||
g_variant_lock (value);
|
||||
|
||||
if (value->state & STATE_SERIALISED)
|
||||
- {
|
||||
- GVariantSerialised serialised = {
|
||||
- value->type_info,
|
||||
- (gpointer) value->contents.serialised.data,
|
||||
- value->size,
|
||||
- value->depth,
|
||||
- };
|
||||
-
|
||||
- n_children = g_variant_serialised_n_children (serialised);
|
||||
- }
|
||||
+ n_children = g_variant_serialised_n_children (
|
||||
+ g_variant_to_serialised (value));
|
||||
else
|
||||
n_children = value->contents.tree.n_children;
|
||||
|
||||
@@ -1061,12 +1074,7 @@ g_variant_get_child_value (GVariant *value,
|
||||
}
|
||||
|
||||
{
|
||||
- GVariantSerialised serialised = {
|
||||
- value->type_info,
|
||||
- (gpointer) value->contents.serialised.data,
|
||||
- value->size,
|
||||
- value->depth,
|
||||
- };
|
||||
+ GVariantSerialised serialised = g_variant_to_serialised (value);
|
||||
GVariantSerialised s_child;
|
||||
GVariant *child;
|
||||
|
||||
@@ -1179,14 +1187,7 @@ g_variant_is_normal_form (GVariant *value)
|
||||
|
||||
if (value->state & STATE_SERIALISED)
|
||||
{
|
||||
- GVariantSerialised serialised = {
|
||||
- value->type_info,
|
||||
- (gpointer) value->contents.serialised.data,
|
||||
- value->size,
|
||||
- value->depth
|
||||
- };
|
||||
-
|
||||
- if (g_variant_serialised_is_normal (serialised))
|
||||
+ if (g_variant_serialised_is_normal (g_variant_to_serialised (value)))
|
||||
value->state |= STATE_TRUSTED;
|
||||
}
|
||||
else
|
||||
--
|
||||
2.24.4
|
||||
|
||||
210
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0002.patch
Normal file
210
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0002.patch
Normal file
@@ -0,0 +1,210 @@
|
||||
From 446e69f5edd72deb2196dee36bbaf8056caf6948 Mon Sep 17 00:00:00 2001
|
||||
From: William Manley <will@stb-tester.com>
|
||||
Date: Wed, 9 Aug 2023 10:39:34 +0000
|
||||
Subject: [PATCH] gvariant-serialiser: Factor out functions for dealing with
|
||||
framing offsets
|
||||
|
||||
This introduces no functional changes.
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/446e69f5edd72deb2196dee36bbaf8056caf6948]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-serialiser.c | 108 +++++++++++++++++++------------------
|
||||
1 file changed, 57 insertions(+), 51 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index 83e9d85..c7c2114 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -633,30 +633,62 @@ gvs_calculate_total_size (gsize body_size,
|
||||
return body_size + 8 * offsets;
|
||||
}
|
||||
|
||||
+struct Offsets
|
||||
+{
|
||||
+ gsize data_size;
|
||||
+
|
||||
+ guchar *array;
|
||||
+ gsize length;
|
||||
+ guint offset_size;
|
||||
+
|
||||
+ gboolean is_normal;
|
||||
+};
|
||||
+
|
||||
static gsize
|
||||
-gvs_variable_sized_array_n_children (GVariantSerialised value)
|
||||
+gvs_offsets_get_offset_n (struct Offsets *offsets,
|
||||
+ gsize n)
|
||||
+{
|
||||
+ return gvs_read_unaligned_le (
|
||||
+ offsets->array + (offsets->offset_size * n), offsets->offset_size);
|
||||
+}
|
||||
+
|
||||
+static struct Offsets
|
||||
+gvs_variable_sized_array_get_frame_offsets (GVariantSerialised value)
|
||||
{
|
||||
+ struct Offsets out = { 0, };
|
||||
gsize offsets_array_size;
|
||||
- gsize offset_size;
|
||||
gsize last_end;
|
||||
|
||||
if (value.size == 0)
|
||||
- return 0;
|
||||
-
|
||||
- offset_size = gvs_get_offset_size (value.size);
|
||||
+ {
|
||||
+ out.is_normal = TRUE;
|
||||
+ return out;
|
||||
+ }
|
||||
|
||||
- last_end = gvs_read_unaligned_le (value.data + value.size -
|
||||
- offset_size, offset_size);
|
||||
+ out.offset_size = gvs_get_offset_size (value.size);
|
||||
+ last_end = gvs_read_unaligned_le (value.data + value.size - out.offset_size,
|
||||
+ out.offset_size);
|
||||
|
||||
if (last_end > value.size)
|
||||
- return 0;
|
||||
+ return out; /* offsets not normal */
|
||||
|
||||
offsets_array_size = value.size - last_end;
|
||||
|
||||
- if (offsets_array_size % offset_size)
|
||||
- return 0;
|
||||
+ if (offsets_array_size % out.offset_size)
|
||||
+ return out; /* offsets not normal */
|
||||
+
|
||||
+ out.data_size = last_end;
|
||||
+ out.array = value.data + last_end;
|
||||
+ out.length = offsets_array_size / out.offset_size;
|
||||
+ out.is_normal = TRUE;
|
||||
|
||||
- return offsets_array_size / offset_size;
|
||||
+ return out;
|
||||
+}
|
||||
+
|
||||
+static gsize
|
||||
+gvs_variable_sized_array_n_children (GVariantSerialised value)
|
||||
+{
|
||||
+ return gvs_variable_sized_array_get_frame_offsets (value).length;
|
||||
}
|
||||
|
||||
static GVariantSerialised
|
||||
@@ -664,8 +696,9 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
|
||||
gsize index_)
|
||||
{
|
||||
GVariantSerialised child = { 0, };
|
||||
- gsize offset_size;
|
||||
- gsize last_end;
|
||||
+
|
||||
+ struct Offsets offsets = gvs_variable_sized_array_get_frame_offsets (value);
|
||||
+
|
||||
gsize start;
|
||||
gsize end;
|
||||
|
||||
@@ -673,18 +706,11 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
|
||||
g_variant_type_info_ref (child.type_info);
|
||||
child.depth = value.depth + 1;
|
||||
|
||||
- offset_size = gvs_get_offset_size (value.size);
|
||||
-
|
||||
- last_end = gvs_read_unaligned_le (value.data + value.size -
|
||||
- offset_size, offset_size);
|
||||
-
|
||||
if (index_ > 0)
|
||||
{
|
||||
guint alignment;
|
||||
|
||||
- start = gvs_read_unaligned_le (value.data + last_end +
|
||||
- (offset_size * (index_ - 1)),
|
||||
- offset_size);
|
||||
+ start = gvs_offsets_get_offset_n (&offsets, index_ - 1);
|
||||
|
||||
g_variant_type_info_query (child.type_info, &alignment, NULL);
|
||||
start += (-start) & alignment;
|
||||
@@ -692,11 +718,9 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
|
||||
else
|
||||
start = 0;
|
||||
|
||||
- end = gvs_read_unaligned_le (value.data + last_end +
|
||||
- (offset_size * index_),
|
||||
- offset_size);
|
||||
+ end = gvs_offsets_get_offset_n (&offsets, index_);
|
||||
|
||||
- if (start < end && end <= value.size && end <= last_end)
|
||||
+ if (start < end && end <= value.size && end <= offsets.data_size)
|
||||
{
|
||||
child.data = value.data + start;
|
||||
child.size = end - start;
|
||||
@@ -768,34 +792,16 @@ static gboolean
|
||||
gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
{
|
||||
GVariantSerialised child = { 0, };
|
||||
- gsize offsets_array_size;
|
||||
- guchar *offsets_array;
|
||||
- guint offset_size;
|
||||
guint alignment;
|
||||
- gsize last_end;
|
||||
- gsize length;
|
||||
gsize offset;
|
||||
gsize i;
|
||||
|
||||
- if (value.size == 0)
|
||||
- return TRUE;
|
||||
-
|
||||
- offset_size = gvs_get_offset_size (value.size);
|
||||
- last_end = gvs_read_unaligned_le (value.data + value.size -
|
||||
- offset_size, offset_size);
|
||||
+ struct Offsets offsets = gvs_variable_sized_array_get_frame_offsets (value);
|
||||
|
||||
- if (last_end > value.size)
|
||||
+ if (!offsets.is_normal)
|
||||
return FALSE;
|
||||
|
||||
- offsets_array_size = value.size - last_end;
|
||||
-
|
||||
- if (offsets_array_size % offset_size)
|
||||
- return FALSE;
|
||||
-
|
||||
- offsets_array = value.data + value.size - offsets_array_size;
|
||||
- length = offsets_array_size / offset_size;
|
||||
-
|
||||
- if (length == 0)
|
||||
+ if (value.size != 0 && offsets.length == 0)
|
||||
return FALSE;
|
||||
|
||||
child.type_info = g_variant_type_info_element (value.type_info);
|
||||
@@ -803,14 +809,14 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
child.depth = value.depth + 1;
|
||||
offset = 0;
|
||||
|
||||
- for (i = 0; i < length; i++)
|
||||
+ for (i = 0; i < offsets.length; i++)
|
||||
{
|
||||
gsize this_end;
|
||||
|
||||
- this_end = gvs_read_unaligned_le (offsets_array + offset_size * i,
|
||||
- offset_size);
|
||||
+ this_end = gvs_read_unaligned_le (offsets.array + offsets.offset_size * i,
|
||||
+ offsets.offset_size);
|
||||
|
||||
- if (this_end < offset || this_end > last_end)
|
||||
+ if (this_end < offset || this_end > offsets.data_size)
|
||||
return FALSE;
|
||||
|
||||
while (offset & alignment)
|
||||
@@ -832,7 +838,7 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
offset = this_end;
|
||||
}
|
||||
|
||||
- g_assert (offset == last_end);
|
||||
+ g_assert (offset == offsets.data_size);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
--
|
||||
2.24.4
|
||||
|
||||
417
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0003.patch
Normal file
417
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0003.patch
Normal file
@@ -0,0 +1,417 @@
|
||||
From ade71fb544391b2e33e1859645726bfee0d5eaaf Mon Sep 17 00:00:00 2001
|
||||
From: William Manley <will@stb-tester.com>
|
||||
Date: Wed, 16 Aug 2023 03:12:21 +0000
|
||||
Subject: [PATCH] gvariant: Don't allow child elements to overlap with each
|
||||
other
|
||||
|
||||
If different elements of a variable sized array can overlap with each
|
||||
other then we can cause a `GVariant` to normalise to a much larger type.
|
||||
|
||||
This commit changes the behaviour of `GVariant` with non-normal form data. If
|
||||
an invalid frame offset is found all subsequent elements are given their
|
||||
default value.
|
||||
|
||||
When retrieving an element at index `n` we scan the frame offsets up to index
|
||||
`n` and if they are not in order we return an element with the default value
|
||||
for that type. This guarantees that elements don't overlap with each
|
||||
other. We remember the offset we've scanned up to so we don't need to
|
||||
repeat this work on subsequent accesses. We skip these checks for trusted
|
||||
data.
|
||||
|
||||
Unfortunately this makes random access of untrusted data O(n) — at least
|
||||
on first access. It doesn't affect the algorithmic complexity of accessing
|
||||
elements in order, such as when using the `GVariantIter` interface. Also:
|
||||
the cost of validation will be amortised as the `GVariant` instance is
|
||||
continued to be used.
|
||||
|
||||
I've implemented this with 4 different functions, 1 for each element size,
|
||||
rather than looping calling `gvs_read_unaligned_le` in the hope that the
|
||||
compiler will find it easy to optimise and should produce fairly tight
|
||||
code.
|
||||
|
||||
Fixes: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/ade71fb544391b2e33e1859645726bfee0d5eaaf]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-core.c | 35 ++++++++++++++++
|
||||
glib/gvariant-serialiser.c | 86 ++++++++++++++++++++++++++++++++++++--
|
||||
glib/gvariant-serialiser.h | 8 ++++
|
||||
glib/tests/gvariant.c | 45 ++++++++++++++++++++
|
||||
4 files changed, 171 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
|
||||
index aa0e0a0..9b51e15 100644
|
||||
--- a/glib/gvariant-core.c
|
||||
+++ b/glib/gvariant-core.c
|
||||
@@ -65,6 +65,7 @@ struct _GVariant
|
||||
{
|
||||
GBytes *bytes;
|
||||
gconstpointer data;
|
||||
+ gsize ordered_offsets_up_to;
|
||||
} serialised;
|
||||
|
||||
struct
|
||||
@@ -162,6 +163,24 @@ struct _GVariant
|
||||
* if .data pointed to the appropriate number of nul
|
||||
* bytes.
|
||||
*
|
||||
+ * .ordered_offsets_up_to: If ordered_offsets_up_to == n this means that all
|
||||
+ * the frame offsets up to and including the frame
|
||||
+ * offset determining the end of element n are in
|
||||
+ * order. This guarantees that the bytes of element
|
||||
+ * n don't overlap with any previous element.
|
||||
+ *
|
||||
+ * For trusted data this is set to G_MAXSIZE and we
|
||||
+ * don't check that the frame offsets are in order.
|
||||
+ *
|
||||
+ * Note: This doesn't imply the offsets are good in
|
||||
+ * any way apart from their ordering. In particular
|
||||
+ * offsets may be out of bounds for this value or
|
||||
+ * may imply that the data overlaps the frame
|
||||
+ * offsets themselves.
|
||||
+ *
|
||||
+ * This field is only relevant for arrays of non
|
||||
+ * fixed width types.
|
||||
+ *
|
||||
* .tree: Only valid when the instance is in tree form.
|
||||
*
|
||||
* Note that accesses from other threads could result in
|
||||
@@ -365,6 +384,7 @@ g_variant_to_serialised (GVariant *value)
|
||||
(gpointer) value->contents.serialised.data,
|
||||
value->size,
|
||||
value->depth,
|
||||
+ value->contents.serialised.ordered_offsets_up_to,
|
||||
};
|
||||
return serialised;
|
||||
}
|
||||
@@ -396,6 +416,7 @@ g_variant_serialise (GVariant *value,
|
||||
serialised.size = value->size;
|
||||
serialised.data = data;
|
||||
serialised.depth = value->depth;
|
||||
+ serialised.ordered_offsets_up_to = 0;
|
||||
|
||||
children = (gpointer *) value->contents.tree.children;
|
||||
n_children = value->contents.tree.n_children;
|
||||
@@ -439,6 +460,15 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
|
||||
g_assert (serialised->size == value->size);
|
||||
serialised->depth = value->depth;
|
||||
|
||||
+ if (value->state & STATE_SERIALISED)
|
||||
+ {
|
||||
+ serialised->ordered_offsets_up_to = value->contents.serialised.ordered_offsets_up_to;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ serialised->ordered_offsets_up_to = 0;
|
||||
+ }
|
||||
+
|
||||
if (serialised->data)
|
||||
/* g_variant_store() is a public API, so it
|
||||
* it will reacquire the lock if it needs to.
|
||||
@@ -481,6 +511,7 @@ g_variant_ensure_serialised (GVariant *value)
|
||||
bytes = g_bytes_new_take (data, value->size);
|
||||
value->contents.serialised.data = g_bytes_get_data (bytes, NULL);
|
||||
value->contents.serialised.bytes = bytes;
|
||||
+ value->contents.serialised.ordered_offsets_up_to = G_MAXSIZE;
|
||||
value->state |= STATE_SERIALISED;
|
||||
}
|
||||
}
|
||||
@@ -561,6 +592,7 @@ g_variant_new_from_bytes (const GVariantType *type,
|
||||
serialised.type_info = value->type_info;
|
||||
serialised.data = (guchar *) g_bytes_get_data (bytes, &serialised.size);
|
||||
serialised.depth = 0;
|
||||
+ serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
|
||||
|
||||
if (!g_variant_serialised_check (serialised))
|
||||
{
|
||||
@@ -610,6 +642,8 @@ g_variant_new_from_bytes (const GVariantType *type,
|
||||
value->contents.serialised.data = g_bytes_get_data (bytes, &value->size);
|
||||
}
|
||||
|
||||
+ value->contents.serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
|
||||
+
|
||||
g_clear_pointer (&owned_bytes, g_bytes_unref);
|
||||
|
||||
return value;
|
||||
@@ -1108,6 +1142,7 @@ g_variant_get_child_value (GVariant *value,
|
||||
child->contents.serialised.bytes =
|
||||
g_bytes_ref (value->contents.serialised.bytes);
|
||||
child->contents.serialised.data = s_child.data;
|
||||
+ child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to;
|
||||
|
||||
return child;
|
||||
}
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index c7c2114..fe0b1a4 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright © 2007, 2008 Ryan Lortie
|
||||
* Copyright © 2010 Codethink Limited
|
||||
+ * Copyright © 2020 William Manley
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -264,6 +265,7 @@ gvs_fixed_sized_maybe_get_child (GVariantSerialised value,
|
||||
value.type_info = g_variant_type_info_element (value.type_info);
|
||||
g_variant_type_info_ref (value.type_info);
|
||||
value.depth++;
|
||||
+ value.ordered_offsets_up_to = 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -295,7 +297,7 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised value,
|
||||
{
|
||||
if (n_children)
|
||||
{
|
||||
- GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1 };
|
||||
+ GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0 };
|
||||
|
||||
gvs_filler (&child, children[0]);
|
||||
}
|
||||
@@ -317,6 +319,7 @@ gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
|
||||
/* proper element size: "Just". recurse to the child. */
|
||||
value.type_info = g_variant_type_info_element (value.type_info);
|
||||
value.depth++;
|
||||
+ value.ordered_offsets_up_to = 0;
|
||||
|
||||
return g_variant_serialised_is_normal (value);
|
||||
}
|
||||
@@ -358,6 +361,7 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value,
|
||||
value.data = NULL;
|
||||
|
||||
value.depth++;
|
||||
+ value.ordered_offsets_up_to = 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -388,7 +392,7 @@ gvs_variable_sized_maybe_serialise (GVariantSerialised value,
|
||||
{
|
||||
if (n_children)
|
||||
{
|
||||
- GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1 };
|
||||
+ GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0 };
|
||||
|
||||
/* write the data for the child. */
|
||||
gvs_filler (&child, children[0]);
|
||||
@@ -408,6 +412,7 @@ gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
|
||||
value.type_info = g_variant_type_info_element (value.type_info);
|
||||
value.size--;
|
||||
value.depth++;
|
||||
+ value.ordered_offsets_up_to = 0;
|
||||
|
||||
return g_variant_serialised_is_normal (value);
|
||||
}
|
||||
@@ -691,6 +696,32 @@ gvs_variable_sized_array_n_children (GVariantSerialised value)
|
||||
return gvs_variable_sized_array_get_frame_offsets (value).length;
|
||||
}
|
||||
|
||||
+/* Find the index of the first out-of-order element in @data, assuming that
|
||||
+ * @data is an array of elements of given @type, starting at index @start and
|
||||
+ * containing a further @len-@start elements. */
|
||||
+#define DEFINE_FIND_UNORDERED(type) \
|
||||
+ static gsize \
|
||||
+ find_unordered_##type (const guint8 *data, gsize start, gsize len) \
|
||||
+ { \
|
||||
+ gsize off; \
|
||||
+ type current, previous; \
|
||||
+ \
|
||||
+ memcpy (&previous, data + start * sizeof (current), sizeof (current)); \
|
||||
+ for (off = (start + 1) * sizeof (current); off < len * sizeof (current); off += sizeof (current)) \
|
||||
+ { \
|
||||
+ memcpy (¤t, data + off, sizeof (current)); \
|
||||
+ if (current < previous) \
|
||||
+ break; \
|
||||
+ previous = current; \
|
||||
+ } \
|
||||
+ return off / sizeof (current) - 1; \
|
||||
+ }
|
||||
+
|
||||
+DEFINE_FIND_UNORDERED (guint8);
|
||||
+DEFINE_FIND_UNORDERED (guint16);
|
||||
+DEFINE_FIND_UNORDERED (guint32);
|
||||
+DEFINE_FIND_UNORDERED (guint64);
|
||||
+
|
||||
static GVariantSerialised
|
||||
gvs_variable_sized_array_get_child (GVariantSerialised value,
|
||||
gsize index_)
|
||||
@@ -706,6 +737,49 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
|
||||
g_variant_type_info_ref (child.type_info);
|
||||
child.depth = value.depth + 1;
|
||||
|
||||
+ /* If the requested @index_ is beyond the set of indices whose framing offsets
|
||||
+ * have been checked, check the remaining offsets to see whether they’re
|
||||
+ * normal (in order, no overlapping array elements). */
|
||||
+ if (index_ > value.ordered_offsets_up_to)
|
||||
+ {
|
||||
+ switch (offsets.offset_size)
|
||||
+ {
|
||||
+ case 1:
|
||||
+ {
|
||||
+ value.ordered_offsets_up_to = find_unordered_guint8 (
|
||||
+ offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ break;
|
||||
+ }
|
||||
+ case 2:
|
||||
+ {
|
||||
+ value.ordered_offsets_up_to = find_unordered_guint16 (
|
||||
+ offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ break;
|
||||
+ }
|
||||
+ case 4:
|
||||
+ {
|
||||
+ value.ordered_offsets_up_to = find_unordered_guint32 (
|
||||
+ offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ break;
|
||||
+ }
|
||||
+ case 8:
|
||||
+ {
|
||||
+ value.ordered_offsets_up_to = find_unordered_guint64 (
|
||||
+ offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ break;
|
||||
+ }
|
||||
+ default:
|
||||
+ /* gvs_get_offset_size() only returns maximum 8 */
|
||||
+ g_assert_not_reached ();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (index_ > value.ordered_offsets_up_to)
|
||||
+ {
|
||||
+ /* Offsets are invalid somewhere, so return an empty child. */
|
||||
+ return child;
|
||||
+ }
|
||||
+
|
||||
if (index_ > 0)
|
||||
{
|
||||
guint alignment;
|
||||
@@ -840,6 +914,9 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
|
||||
g_assert (offset == offsets.data_size);
|
||||
|
||||
+ /* All offsets have now been checked. */
|
||||
+ value.ordered_offsets_up_to = G_MAXSIZE;
|
||||
+
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -1072,7 +1149,7 @@ gvs_tuple_is_normal (GVariantSerialised value)
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
const GVariantMemberInfo *member_info;
|
||||
- GVariantSerialised child;
|
||||
+ GVariantSerialised child = { 0, };
|
||||
gsize fixed_size;
|
||||
guint alignment;
|
||||
gsize end;
|
||||
@@ -1132,6 +1209,9 @@ gvs_tuple_is_normal (GVariantSerialised value)
|
||||
offset = end;
|
||||
}
|
||||
|
||||
+ /* All element bounds have been checked above. */
|
||||
+ value.ordered_offsets_up_to = G_MAXSIZE;
|
||||
+
|
||||
{
|
||||
gsize fixed_size;
|
||||
guint alignment;
|
||||
diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
|
||||
index 81343e9..99d18ef 100644
|
||||
--- a/glib/gvariant-serialiser.h
|
||||
+++ b/glib/gvariant-serialiser.h
|
||||
@@ -29,6 +29,14 @@ typedef struct
|
||||
guchar *data;
|
||||
gsize size;
|
||||
gsize depth; /* same semantics as GVariant.depth */
|
||||
+ /* If ordered_offsets_up_to == n this means that all the frame offsets up to and
|
||||
+ * including the frame offset determining the end of element n are in order.
|
||||
+ * This guarantees that the bytes of element n don't overlap with any previous
|
||||
+ * element.
|
||||
+ *
|
||||
+ * This is both read and set by g_variant_serialised_get_child for arrays of
|
||||
+ * non-fixed-width types */
|
||||
+ gsize ordered_offsets_up_to;
|
||||
} GVariantSerialised;
|
||||
|
||||
/* deserialisation */
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index 0e5ec8e..967e9a1 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
+ * Copyright © 2020 William Manley
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -1283,6 +1284,7 @@ random_instance_filler (GVariantSerialised *serialised,
|
||||
serialised->size = instance->size;
|
||||
|
||||
serialised->depth = 0;
|
||||
+ serialised->ordered_offsets_up_to = 0;
|
||||
|
||||
g_assert_true (serialised->type_info == instance->type_info);
|
||||
g_assert_cmpuint (serialised->size, ==, instance->size);
|
||||
@@ -5039,6 +5041,47 @@ test_normal_checking_array_offsets (void)
|
||||
g_variant_unref (variant);
|
||||
}
|
||||
|
||||
+/* This is a regression test that we can't have non-normal values that take up
|
||||
+ * significantly more space than the normal equivalent, by specifying the
|
||||
+ * offset table entries so that array elements overlap.
|
||||
+ *
|
||||
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_832242 */
|
||||
+static void
|
||||
+test_normal_checking_array_offsets2 (void)
|
||||
+{
|
||||
+ const guint8 data[] = {
|
||||
+ 'h', 'i', '\0',
|
||||
+ 0x03, 0x00, 0x03,
|
||||
+ 0x06, 0x00, 0x06,
|
||||
+ 0x09, 0x00, 0x09,
|
||||
+ 0x0c, 0x00, 0x0c,
|
||||
+ 0x0f, 0x00, 0x0f,
|
||||
+ 0x12, 0x00, 0x12,
|
||||
+ 0x15, 0x00, 0x15,
|
||||
+ };
|
||||
+ gsize size = sizeof (data);
|
||||
+ const GVariantType *aaaaaaas = G_VARIANT_TYPE ("aaaaaaas");
|
||||
+ GVariant *variant = NULL;
|
||||
+ GVariant *normal_variant = NULL;
|
||||
+ GVariant *expected = NULL;
|
||||
+
|
||||
+ variant = g_variant_new_from_data (aaaaaaas, data, size, FALSE, NULL, NULL);
|
||||
+ g_assert_nonnull (variant);
|
||||
+
|
||||
+ normal_variant = g_variant_get_normal_form (variant);
|
||||
+ g_assert_nonnull (normal_variant);
|
||||
+ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 2);
|
||||
+
|
||||
+ expected = g_variant_new_parsed (
|
||||
+ "[[[[[[['hi', '', ''], [], []], [], []], [], []], [], []], [], []], [], []]");
|
||||
+ g_assert_cmpvariant (expected, variant);
|
||||
+ g_assert_cmpvariant (expected, normal_variant);
|
||||
+
|
||||
+ g_variant_unref (expected);
|
||||
+ g_variant_unref (normal_variant);
|
||||
+ g_variant_unref (variant);
|
||||
+}
|
||||
+
|
||||
/* Test that a tuple with invalidly large values in its offset table is
|
||||
* normalised successfully without looping infinitely. */
|
||||
static void
|
||||
@@ -5206,6 +5249,8 @@ main (int argc, char **argv)
|
||||
test_normal_checking_tuples);
|
||||
g_test_add_func ("/gvariant/normal-checking/array-offsets",
|
||||
test_normal_checking_array_offsets);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/array-offsets2",
|
||||
+ test_normal_checking_array_offsets2);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
|
||||
test_normal_checking_tuple_offsets);
|
||||
g_test_add_func ("/gvariant/normal-checking/empty-object-path",
|
||||
--
|
||||
2.24.4
|
||||
|
||||
113
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0004.patch
Normal file
113
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0004.patch
Normal file
@@ -0,0 +1,113 @@
|
||||
From 345cae9c1aa7bf6752039225ef4c8d8d69fa8d76 Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Fri, 11 Aug 2023 04:09:12 +0000
|
||||
Subject: [PATCH] gvariant-serialiser: Factor out code to get bounds of a tuple
|
||||
member
|
||||
|
||||
This introduces no functional changes.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/345cae9c1aa7bf6752039225ef4c8d8d69fa8d76]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-serialiser.c | 73 ++++++++++++++++++++++++--------------
|
||||
1 file changed, 46 insertions(+), 27 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index fe0b1a4..6f9b366 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -942,6 +942,51 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
* for the tuple. See the notes in gvarianttypeinfo.h.
|
||||
*/
|
||||
|
||||
+static void
|
||||
+gvs_tuple_get_member_bounds (GVariantSerialised value,
|
||||
+ gsize index_,
|
||||
+ gsize offset_size,
|
||||
+ gsize *out_member_start,
|
||||
+ gsize *out_member_end)
|
||||
+{
|
||||
+ const GVariantMemberInfo *member_info;
|
||||
+ gsize member_start, member_end;
|
||||
+
|
||||
+ member_info = g_variant_type_info_member_info (value.type_info, index_);
|
||||
+
|
||||
+ if (member_info->i + 1)
|
||||
+ member_start = gvs_read_unaligned_le (value.data + value.size -
|
||||
+ offset_size * (member_info->i + 1),
|
||||
+ offset_size);
|
||||
+ else
|
||||
+ member_start = 0;
|
||||
+
|
||||
+ member_start += member_info->a;
|
||||
+ member_start &= member_info->b;
|
||||
+ member_start |= member_info->c;
|
||||
+
|
||||
+ if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST)
|
||||
+ member_end = value.size - offset_size * (member_info->i + 1);
|
||||
+
|
||||
+ else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
|
||||
+ {
|
||||
+ gsize fixed_size;
|
||||
+
|
||||
+ g_variant_type_info_query (member_info->type_info, NULL, &fixed_size);
|
||||
+ member_end = member_start + fixed_size;
|
||||
+ }
|
||||
+
|
||||
+ else /* G_VARIANT_MEMBER_ENDING_OFFSET */
|
||||
+ member_end = gvs_read_unaligned_le (value.data + value.size -
|
||||
+ offset_size * (member_info->i + 2),
|
||||
+ offset_size);
|
||||
+
|
||||
+ if (out_member_start != NULL)
|
||||
+ *out_member_start = member_start;
|
||||
+ if (out_member_end != NULL)
|
||||
+ *out_member_end = member_end;
|
||||
+}
|
||||
+
|
||||
static gsize
|
||||
gvs_tuple_n_children (GVariantSerialised value)
|
||||
{
|
||||
@@ -997,33 +1042,7 @@ gvs_tuple_get_child (GVariantSerialised value,
|
||||
}
|
||||
}
|
||||
|
||||
- if (member_info->i + 1)
|
||||
- start = gvs_read_unaligned_le (value.data + value.size -
|
||||
- offset_size * (member_info->i + 1),
|
||||
- offset_size);
|
||||
- else
|
||||
- start = 0;
|
||||
-
|
||||
- start += member_info->a;
|
||||
- start &= member_info->b;
|
||||
- start |= member_info->c;
|
||||
-
|
||||
- if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_LAST)
|
||||
- end = value.size - offset_size * (member_info->i + 1);
|
||||
-
|
||||
- else if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
|
||||
- {
|
||||
- gsize fixed_size;
|
||||
-
|
||||
- g_variant_type_info_query (child.type_info, NULL, &fixed_size);
|
||||
- end = start + fixed_size;
|
||||
- child.size = fixed_size;
|
||||
- }
|
||||
-
|
||||
- else /* G_VARIANT_MEMBER_ENDING_OFFSET */
|
||||
- end = gvs_read_unaligned_le (value.data + value.size -
|
||||
- offset_size * (member_info->i + 2),
|
||||
- offset_size);
|
||||
+ gvs_tuple_get_member_bounds (value, index_, offset_size, &start, &end);
|
||||
|
||||
/* The child should not extend into the offset table. */
|
||||
if (index_ != g_variant_type_info_n_members (value.type_info) - 1)
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
From 73d0aa81c2575a5c9ae77dcb94da919579014fc0 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Fri, 11 Aug 2023 04:13:02 +0000
|
||||
Subject: [PATCH] gvariant-serialiser: Rework child size calculation
|
||||
|
||||
This reduces a few duplicate calls to `g_variant_type_info_query()` and
|
||||
explains why they’re needed.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/73d0aa81c2575a5c9ae77dcb94da919579014fc0]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-serialiser.c | 31 +++++++++----------------------
|
||||
1 file changed, 9 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index 6f9b366..fb75923 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -1007,14 +1007,18 @@ gvs_tuple_get_child (GVariantSerialised value,
|
||||
child.depth = value.depth + 1;
|
||||
offset_size = gvs_get_offset_size (value.size);
|
||||
|
||||
+ /* Ensure the size is set for fixed-sized children, or
|
||||
+ * g_variant_serialised_check() will fail, even if we return
|
||||
+ * (child.data == NULL) to indicate an error. */
|
||||
+ if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_FIXED)
|
||||
+ g_variant_type_info_query (child.type_info, NULL, &child.size);
|
||||
+
|
||||
/* tuples are the only (potentially) fixed-sized containers, so the
|
||||
* only ones that have to deal with the possibility of having %NULL
|
||||
* data with a non-zero %size if errors occurred elsewhere.
|
||||
*/
|
||||
if G_UNLIKELY (value.data == NULL && value.size != 0)
|
||||
{
|
||||
- g_variant_type_info_query (child.type_info, NULL, &child.size);
|
||||
-
|
||||
/* this can only happen in fixed-sized tuples,
|
||||
* so the child must also be fixed sized.
|
||||
*/
|
||||
@@ -1032,29 +1036,12 @@ gvs_tuple_get_child (GVariantSerialised value,
|
||||
else
|
||||
{
|
||||
if (offset_size * (member_info->i + 1) > value.size)
|
||||
- {
|
||||
- /* if the child is fixed size, return its size.
|
||||
- * if child is not fixed-sized, return size = 0.
|
||||
- */
|
||||
- g_variant_type_info_query (child.type_info, NULL, &child.size);
|
||||
-
|
||||
- return child;
|
||||
- }
|
||||
+ return child;
|
||||
}
|
||||
|
||||
- gvs_tuple_get_member_bounds (value, index_, offset_size, &start, &end);
|
||||
-
|
||||
/* The child should not extend into the offset table. */
|
||||
- if (index_ != g_variant_type_info_n_members (value.type_info) - 1)
|
||||
- {
|
||||
- GVariantSerialised last_child;
|
||||
- last_child = gvs_tuple_get_child (value,
|
||||
- g_variant_type_info_n_members (value.type_info) - 1);
|
||||
- last_end = last_child.data + last_child.size - value.data;
|
||||
- g_variant_type_info_unref (last_child.type_info);
|
||||
- }
|
||||
- else
|
||||
- last_end = end;
|
||||
+ gvs_tuple_get_member_bounds (value, index_, offset_size, &start, &end);
|
||||
+ gvs_tuple_get_member_bounds (value, g_variant_type_info_n_members (value.type_info) - 1, offset_size, NULL, &last_end);
|
||||
|
||||
if (start < end && end <= value.size && end <= last_end)
|
||||
{
|
||||
--
|
||||
2.24.4
|
||||
|
||||
396
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0006.patch
Normal file
396
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0006.patch
Normal file
@@ -0,0 +1,396 @@
|
||||
From 7cf6f5b69146d20948d42f0c476688fe17fef787 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Wed, 16 Aug 2023 12:09:06 +0000
|
||||
Subject: [PATCH] gvariant: Don't allow child elements of a tuple to overlap
|
||||
each other
|
||||
|
||||
This is similar to the earlier commit which prevents child elements of a
|
||||
variable-sized array from overlapping each other, but this time for
|
||||
tuples. It is based heavily on ideas by William Manley.
|
||||
|
||||
Tuples are slightly different from variable-sized arrays in that they
|
||||
contain a mixture of fixed and variable sized elements. All but one of
|
||||
the variable sized elements have an entry in the frame offsets table.
|
||||
This means that if we were to just check the ordering of the frame
|
||||
offsets table, the variable sized elements could still overlap
|
||||
interleaving fixed sized elements, which would be bad.
|
||||
|
||||
Therefore we have to check the elements rather than the frame offsets.
|
||||
|
||||
The logic of checking the elements up to the index currently being
|
||||
requested, and caching the result in `ordered_offsets_up_to`, means that
|
||||
the algorithmic cost implications are the same for this commit as for
|
||||
variable-sized arrays: an O(N) cost for these checks is amortised out
|
||||
over N accesses to O(1) per access.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Fixes: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/7cf6f5b69146d20948d42f0c476688fe17fef787]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-core.c | 6 +-
|
||||
glib/gvariant-serialiser.c | 40 ++++++++
|
||||
glib/gvariant-serialiser.h | 7 +-
|
||||
glib/gvariant.c | 1 +
|
||||
glib/tests/gvariant.c | 181 +++++++++++++++++++++++++++++++++++++
|
||||
5 files changed, 232 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
|
||||
index 9b51e15..b951cd9 100644
|
||||
--- a/glib/gvariant-core.c
|
||||
+++ b/glib/gvariant-core.c
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright © 2007, 2008 Ryan Lortie
|
||||
* Copyright © 2010 Codethink Limited
|
||||
+ * Copyright © 2022 Endless OS Foundation, LLC
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -179,7 +180,7 @@ struct _GVariant
|
||||
* offsets themselves.
|
||||
*
|
||||
* This field is only relevant for arrays of non
|
||||
- * fixed width types.
|
||||
+ * fixed width types and for tuples.
|
||||
*
|
||||
* .tree: Only valid when the instance is in tree form.
|
||||
*
|
||||
@@ -1117,6 +1118,9 @@ g_variant_get_child_value (GVariant *value,
|
||||
*/
|
||||
s_child = g_variant_serialised_get_child (serialised, index_);
|
||||
|
||||
+ /* Update the cached ordered_offsets_up_to, since @serialised will be thrown away when this function exits */
|
||||
+ value->contents.serialised.ordered_offsets_up_to = MAX (value->contents.serialised.ordered_offsets_up_to, serialised.ordered_offsets_up_to);
|
||||
+
|
||||
/* Check whether this would cause nesting too deep. If so, return a fake
|
||||
* child. The only situation we expect this to happen in is with a variant,
|
||||
* as all other deeply-nested types have a static type, and hence should
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index fb75923..cd4a3e6 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -942,6 +942,10 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
* for the tuple. See the notes in gvarianttypeinfo.h.
|
||||
*/
|
||||
|
||||
+/* Note: This doesn’t guarantee that @out_member_end >= @out_member_start; that
|
||||
+ * condition may not hold true for invalid serialised variants. The caller is
|
||||
+ * responsible for checking the returned values and handling invalid ones
|
||||
+ * appropriately. */
|
||||
static void
|
||||
gvs_tuple_get_member_bounds (GVariantSerialised value,
|
||||
gsize index_,
|
||||
@@ -1028,6 +1032,42 @@ gvs_tuple_get_child (GVariantSerialised value,
|
||||
return child;
|
||||
}
|
||||
|
||||
+ /* If the requested @index_ is beyond the set of indices whose framing offsets
|
||||
+ * have been checked, check the remaining offsets to see whether they’re
|
||||
+ * normal (in order, no overlapping tuple elements).
|
||||
+ *
|
||||
+ * Unlike the checks in gvs_variable_sized_array_get_child(), we have to check
|
||||
+ * all the tuple *elements* here, not just all the framing offsets, since
|
||||
+ * tuples contain a mix of elements which use framing offsets and ones which
|
||||
+ * don’t. None of them are allowed to overlap. */
|
||||
+ if (index_ > value.ordered_offsets_up_to)
|
||||
+ {
|
||||
+ gsize i, prev_i_end = 0;
|
||||
+
|
||||
+ if (value.ordered_offsets_up_to > 0)
|
||||
+ gvs_tuple_get_member_bounds (value, value.ordered_offsets_up_to - 1, offset_size, NULL, &prev_i_end);
|
||||
+
|
||||
+ for (i = value.ordered_offsets_up_to; i <= index_; i++)
|
||||
+ {
|
||||
+ gsize i_start, i_end;
|
||||
+
|
||||
+ gvs_tuple_get_member_bounds (value, i, offset_size, &i_start, &i_end);
|
||||
+
|
||||
+ if (i_start > i_end || i_start < prev_i_end || i_end > value.size)
|
||||
+ break;
|
||||
+
|
||||
+ prev_i_end = i_end;
|
||||
+ }
|
||||
+
|
||||
+ value.ordered_offsets_up_to = i - 1;
|
||||
+ }
|
||||
+
|
||||
+ if (index_ > value.ordered_offsets_up_to)
|
||||
+ {
|
||||
+ /* Offsets are invalid somewhere, so return an empty child. */
|
||||
+ return child;
|
||||
+ }
|
||||
+
|
||||
if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET)
|
||||
{
|
||||
if (offset_size * (member_info->i + 2) > value.size)
|
||||
diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
|
||||
index 99d18ef..144aec8 100644
|
||||
--- a/glib/gvariant-serialiser.h
|
||||
+++ b/glib/gvariant-serialiser.h
|
||||
@@ -34,8 +34,11 @@ typedef struct
|
||||
* This guarantees that the bytes of element n don't overlap with any previous
|
||||
* element.
|
||||
*
|
||||
- * This is both read and set by g_variant_serialised_get_child for arrays of
|
||||
- * non-fixed-width types */
|
||||
+ * This is both read and set by g_variant_serialised_get_child() for arrays of
|
||||
+ * non-fixed-width types, and for tuples.
|
||||
+ *
|
||||
+ * Even when dealing with tuples, @ordered_offsets_up_to is an element index,
|
||||
+ * rather than an index into the frame offsets. */
|
||||
gsize ordered_offsets_up_to;
|
||||
} GVariantSerialised;
|
||||
|
||||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||||
index d6f68a9..cdb428e 100644
|
||||
--- a/glib/gvariant.c
|
||||
+++ b/glib/gvariant.c
|
||||
@@ -5945,6 +5945,7 @@ g_variant_byteswap (GVariant *value)
|
||||
serialised.type_info = g_variant_get_type_info (trusted);
|
||||
serialised.size = g_variant_get_size (trusted);
|
||||
serialised.data = g_malloc (serialised.size);
|
||||
+ serialised.ordered_offsets_up_to = G_MAXSIZE; /* operating on the normal form */
|
||||
g_variant_store (trusted, serialised.data);
|
||||
g_variant_unref (trusted);
|
||||
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index 967e9a1..a84b02e 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright © 2010 Codethink Limited
|
||||
* Copyright © 2020 William Manley
|
||||
+ * Copyright © 2022 Endless OS Foundation, LLC
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@@ -1451,6 +1452,7 @@ test_maybe (void)
|
||||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
+ serialised.ordered_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised,
|
||||
random_instance_filler,
|
||||
@@ -1574,6 +1576,7 @@ test_array (void)
|
||||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
+ serialised.ordered_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||||
(gpointer *) instances, n_children);
|
||||
@@ -1738,6 +1741,7 @@ test_tuple (void)
|
||||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
+ serialised.ordered_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||||
(gpointer *) instances, n_children);
|
||||
@@ -1834,6 +1838,7 @@ test_variant (void)
|
||||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
+ serialised.ordered_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||||
(gpointer *) &instance, 1);
|
||||
@@ -5106,6 +5111,176 @@ test_normal_checking_tuple_offsets (void)
|
||||
g_variant_unref (variant);
|
||||
}
|
||||
|
||||
+/* This is a regression test that we can't have non-normal values that take up
|
||||
+ * significantly more space than the normal equivalent, by specifying the
|
||||
+ * offset table entries so that tuple elements overlap.
|
||||
+ *
|
||||
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_838503 and
|
||||
+ * https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_838513 */
|
||||
+static void
|
||||
+test_normal_checking_tuple_offsets2 (void)
|
||||
+{
|
||||
+ const GVariantType *data_type = G_VARIANT_TYPE ("(yyaiyyaiyy)");
|
||||
+ const guint8 data[] = {
|
||||
+ 0x12, 0x34, 0x56, 0x78, 0x01,
|
||||
+ /*
|
||||
+ ^───────────────────┘
|
||||
+
|
||||
+ ^^^^^^^^^^ 1st yy
|
||||
+ ^^^^^^^^^^ 2nd yy
|
||||
+ ^^^^^^^^^^ 3rd yy
|
||||
+ ^^^^ Framing offsets
|
||||
+ */
|
||||
+
|
||||
+ /* If this variant was encoded normally, it would be something like this:
|
||||
+ * 0x12, 0x34, pad, pad, [array bytes], 0x56, 0x78, pad, pad, [array bytes], 0x9A, 0xBC, 0xXX
|
||||
+ * ^─────────────────────────────────────────────────────┘
|
||||
+ *
|
||||
+ * ^^^^^^^^^^ 1st yy
|
||||
+ * ^^^^^^^^^^ 2nd yy
|
||||
+ * ^^^^^^^^^^ 3rd yy
|
||||
+ * ^^^^ Framing offsets
|
||||
+ */
|
||||
+ };
|
||||
+ gsize size = sizeof (data);
|
||||
+ GVariant *variant = NULL;
|
||||
+ GVariant *normal_variant = NULL;
|
||||
+ GVariant *expected = NULL;
|
||||
+
|
||||
+ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
|
||||
+ g_assert_nonnull (variant);
|
||||
+
|
||||
+ normal_variant = g_variant_get_normal_form (variant);
|
||||
+ g_assert_nonnull (normal_variant);
|
||||
+ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3);
|
||||
+
|
||||
+ expected = g_variant_new_parsed (
|
||||
+ "@(yyaiyyaiyy) (0x12, 0x34, [], 0x00, 0x00, [], 0x00, 0x00)");
|
||||
+ g_assert_cmpvariant (expected, variant);
|
||||
+ g_assert_cmpvariant (expected, normal_variant);
|
||||
+
|
||||
+ g_variant_unref (expected);
|
||||
+ g_variant_unref (normal_variant);
|
||||
+ g_variant_unref (variant);
|
||||
+}
|
||||
+
|
||||
+/* This is a regression test that overlapping entries in the offset table are
|
||||
+ * decoded consistently, even though they’re non-normal.
|
||||
+ *
|
||||
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_910935 */
|
||||
+static void
|
||||
+test_normal_checking_tuple_offsets3 (void)
|
||||
+{
|
||||
+ /* The expected decoding of this non-normal byte stream is complex. See
|
||||
+ * section 2.7.3 (Handling Non-Normal Serialised Data) of the GVariant
|
||||
+ * specification.
|
||||
+ *
|
||||
+ * The rule “Child Values Overlapping Framing Offsets” from the specification
|
||||
+ * says that the first `ay` must be decoded as `[0x01]` even though it
|
||||
+ * overlaps the first byte of the offset table. However, since commit
|
||||
+ * 7eedcd76f7d5b8c98fa60013e1fe6e960bf19df3, GLib explicitly doesn’t allow
|
||||
+ * this as it’s exploitable. So the first `ay` must be given a default value.
|
||||
+ *
|
||||
+ * The second and third `ay`s must be given default values because of rule
|
||||
+ * “End Boundary Precedes Start Boundary”.
|
||||
+ *
|
||||
+ * The `i` must be given a default value because of rule “Start or End
|
||||
+ * Boundary of a Child Falls Outside the Container”.
|
||||
+ */
|
||||
+ const GVariantType *data_type = G_VARIANT_TYPE ("(ayayiay)");
|
||||
+ const guint8 data[] = {
|
||||
+ 0x01, 0x00, 0x02,
|
||||
+ /*
|
||||
+ ^──┘
|
||||
+
|
||||
+ ^^^^^^^^^^ 1st ay, bytes 0-2 (but given a default value anyway, see above)
|
||||
+ 2nd ay, bytes 2-0
|
||||
+ i, bytes 0-4
|
||||
+ 3rd ay, bytes 4-1
|
||||
+ ^^^^^^^^^^ Framing offsets
|
||||
+ */
|
||||
+ };
|
||||
+ gsize size = sizeof (data);
|
||||
+ GVariant *variant = NULL;
|
||||
+ GVariant *normal_variant = NULL;
|
||||
+ GVariant *expected = NULL;
|
||||
+
|
||||
+ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
|
||||
+ g_assert_nonnull (variant);
|
||||
+
|
||||
+ g_assert_false (g_variant_is_normal_form (variant));
|
||||
+
|
||||
+ normal_variant = g_variant_get_normal_form (variant);
|
||||
+ g_assert_nonnull (normal_variant);
|
||||
+ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3);
|
||||
+
|
||||
+ expected = g_variant_new_parsed ("@(ayayiay) ([], [], 0, [])");
|
||||
+ g_assert_cmpvariant (expected, variant);
|
||||
+ g_assert_cmpvariant (expected, normal_variant);
|
||||
+
|
||||
+ g_variant_unref (expected);
|
||||
+ g_variant_unref (normal_variant);
|
||||
+ g_variant_unref (variant);
|
||||
+}
|
||||
+
|
||||
+/* This is a regression test that overlapping entries in the offset table are
|
||||
+ * decoded consistently, even though they’re non-normal.
|
||||
+ *
|
||||
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/2121#note_910935 */
|
||||
+static void
|
||||
+test_normal_checking_tuple_offsets4 (void)
|
||||
+{
|
||||
+ /* The expected decoding of this non-normal byte stream is complex. See
|
||||
+ * section 2.7.3 (Handling Non-Normal Serialised Data) of the GVariant
|
||||
+ * specification.
|
||||
+ *
|
||||
+ * The rule “Child Values Overlapping Framing Offsets” from the specification
|
||||
+ * says that the first `ay` must be decoded as `[0x01]` even though it
|
||||
+ * overlaps the first byte of the offset table. However, since commit
|
||||
+ * 7eedcd76f7d5b8c98fa60013e1fe6e960bf19df3, GLib explicitly doesn’t allow
|
||||
+ * this as it’s exploitable. So the first `ay` must be given a default value.
|
||||
+ *
|
||||
+ * The second `ay` must be given a default value because of rule “End Boundary
|
||||
+ * Precedes Start Boundary”.
|
||||
+ *
|
||||
+ * The third `ay` must be given a default value because its framing offsets
|
||||
+ * overlap that of the first `ay`.
|
||||
+ */
|
||||
+ const GVariantType *data_type = G_VARIANT_TYPE ("(ayayay)");
|
||||
+ const guint8 data[] = {
|
||||
+ 0x01, 0x00, 0x02,
|
||||
+ /*
|
||||
+ ^──┘
|
||||
+
|
||||
+ ^^^^^^^^^^ 1st ay, bytes 0-2 (but given a default value anyway, see above)
|
||||
+ 2nd ay, bytes 2-0
|
||||
+ 3rd ay, bytes 0-1
|
||||
+ ^^^^^^^^^^ Framing offsets
|
||||
+ */
|
||||
+ };
|
||||
+ gsize size = sizeof (data);
|
||||
+ GVariant *variant = NULL;
|
||||
+ GVariant *normal_variant = NULL;
|
||||
+ GVariant *expected = NULL;
|
||||
+
|
||||
+ variant = g_variant_new_from_data (data_type, data, size, FALSE, NULL, NULL);
|
||||
+ g_assert_nonnull (variant);
|
||||
+
|
||||
+ g_assert_false (g_variant_is_normal_form (variant));
|
||||
+
|
||||
+ normal_variant = g_variant_get_normal_form (variant);
|
||||
+ g_assert_nonnull (normal_variant);
|
||||
+ g_assert_cmpuint (g_variant_get_size (normal_variant), <=, size * 3);
|
||||
+
|
||||
+ expected = g_variant_new_parsed ("@(ayayay) ([], [], [])");
|
||||
+ g_assert_cmpvariant (expected, variant);
|
||||
+ g_assert_cmpvariant (expected, normal_variant);
|
||||
+
|
||||
+ g_variant_unref (expected);
|
||||
+ g_variant_unref (normal_variant);
|
||||
+ g_variant_unref (variant);
|
||||
+}
|
||||
+
|
||||
/* Test that an empty object path is normalised successfully to the base object
|
||||
* path, ‘/’. */
|
||||
static void
|
||||
@@ -5253,6 +5428,12 @@ main (int argc, char **argv)
|
||||
test_normal_checking_array_offsets2);
|
||||
g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
|
||||
test_normal_checking_tuple_offsets);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets2",
|
||||
+ test_normal_checking_tuple_offsets2);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets3",
|
||||
+ test_normal_checking_tuple_offsets3);
|
||||
+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets4",
|
||||
+ test_normal_checking_tuple_offsets4);
|
||||
g_test_add_func ("/gvariant/normal-checking/empty-object-path",
|
||||
test_normal_checking_empty_object_path);
|
||||
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
From e6490c84e84ba9f182fbd83b51ff4f9f5a0a1793 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Wed, 16 Aug 2023 03:42:47 +0000
|
||||
Subject: [PATCH] gvariant: Port g_variant_deep_copy() to count its iterations
|
||||
directly
|
||||
|
||||
This is equivalent to what `GVariantIter` does, but it means that
|
||||
`g_variant_deep_copy()` is making its own `g_variant_get_child_value()`
|
||||
calls.
|
||||
|
||||
This will be useful in an upcoming commit, where those child values will
|
||||
be inspected a little more deeply.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/e6490c84e84ba9f182fbd83b51ff4f9f5a0a1793]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant.c | 7 +++----
|
||||
1 file changed, 3 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||||
index cdb428e..fdd36be 100644
|
||||
--- a/glib/gvariant.c
|
||||
+++ b/glib/gvariant.c
|
||||
@@ -5799,14 +5799,13 @@ g_variant_deep_copy (GVariant *value)
|
||||
case G_VARIANT_CLASS_VARIANT:
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
- GVariantIter iter;
|
||||
- GVariant *child;
|
||||
+ gsize i, n_children;
|
||||
|
||||
g_variant_builder_init (&builder, g_variant_get_type (value));
|
||||
- g_variant_iter_init (&iter, value);
|
||||
|
||||
- while ((child = g_variant_iter_next_value (&iter)))
|
||||
+ for (i = 0, n_children = g_variant_n_children (value); i < n_children; i++)
|
||||
{
|
||||
+ GVariant *child = g_variant_get_child_value (value, i);
|
||||
g_variant_builder_add_value (&builder, g_variant_deep_copy (child));
|
||||
g_variant_unref (child);
|
||||
}
|
||||
--
|
||||
2.24.4
|
||||
|
||||
394
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0008.patch
Normal file
394
meta/recipes-core/glib-2.0/glib-2.0/CVE-2023-32665-0008.patch
Normal file
@@ -0,0 +1,394 @@
|
||||
From d1a293c4e29880b8d17bb826c9a426a440ca4a91 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Thu, 17 Aug 2023 01:30:38 +0000
|
||||
Subject: [PATCH] gvariant: Track checked and ordered offsets independently
|
||||
|
||||
The past few commits introduced the concept of known-good offsets in the
|
||||
offset table (which is used for variable-width arrays and tuples).
|
||||
Good offsets are ones which are non-overlapping with all the previous
|
||||
offsets in the table.
|
||||
|
||||
If a bad offset is encountered when indexing into the array or tuple,
|
||||
the cached known-good offset index will not be increased. In this way,
|
||||
all child variants at and beyond the first bad offset can be returned as
|
||||
default values rather than dereferencing potentially invalid data.
|
||||
|
||||
In this case, there was no information about the fact that the indexes
|
||||
between the highest known-good index and the requested one had been
|
||||
checked already. That could lead to a pathological case where an offset
|
||||
table with an invalid first offset is repeatedly checked in full when
|
||||
trying to access higher-indexed children.
|
||||
|
||||
Avoid that by storing the index of the highest checked offset in the
|
||||
table, as well as the index of the highest good/ordered offset.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/d1a293c4e29880b8d17bb826c9a426a440ca4a91]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant-core.c | 28 ++++++++++++++++++++++++
|
||||
glib/gvariant-serialiser.c | 44 +++++++++++++++++++++++++++-----------
|
||||
glib/gvariant-serialiser.h | 9 ++++++++
|
||||
glib/gvariant.c | 1 +
|
||||
glib/tests/gvariant.c | 5 +++++
|
||||
5 files changed, 75 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c
|
||||
index b951cd9..1b9d5cc 100644
|
||||
--- a/glib/gvariant-core.c
|
||||
+++ b/glib/gvariant-core.c
|
||||
@@ -67,6 +67,7 @@ struct _GVariant
|
||||
GBytes *bytes;
|
||||
gconstpointer data;
|
||||
gsize ordered_offsets_up_to;
|
||||
+ gsize checked_offsets_up_to;
|
||||
} serialised;
|
||||
|
||||
struct
|
||||
@@ -182,6 +183,24 @@ struct _GVariant
|
||||
* This field is only relevant for arrays of non
|
||||
* fixed width types and for tuples.
|
||||
*
|
||||
+ * .checked_offsets_up_to: Similarly to .ordered_offsets_up_to, this stores
|
||||
+ * the index of the highest element, n, whose frame
|
||||
+ * offsets (and all the preceding frame offsets)
|
||||
+ * have been checked for validity.
|
||||
+ *
|
||||
+ * It is always the case that
|
||||
+ * .checked_offsets_up_to ≥ .ordered_offsets_up_to.
|
||||
+ *
|
||||
+ * If .checked_offsets_up_to == .ordered_offsets_up_to,
|
||||
+ * then a bad offset has not been found so far.
|
||||
+ *
|
||||
+ * If .checked_offsets_up_to > .ordered_offsets_up_to,
|
||||
+ * then a bad offset has been found at
|
||||
+ * (.ordered_offsets_up_to + 1).
|
||||
+ *
|
||||
+ * This field is only relevant for arrays of non
|
||||
+ * fixed width types and for tuples.
|
||||
+ *
|
||||
* .tree: Only valid when the instance is in tree form.
|
||||
*
|
||||
* Note that accesses from other threads could result in
|
||||
@@ -386,6 +405,7 @@ g_variant_to_serialised (GVariant *value)
|
||||
value->size,
|
||||
value->depth,
|
||||
value->contents.serialised.ordered_offsets_up_to,
|
||||
+ value->contents.serialised.checked_offsets_up_to,
|
||||
};
|
||||
return serialised;
|
||||
}
|
||||
@@ -418,6 +438,7 @@ g_variant_serialise (GVariant *value,
|
||||
serialised.data = data;
|
||||
serialised.depth = value->depth;
|
||||
serialised.ordered_offsets_up_to = 0;
|
||||
+ serialised.checked_offsets_up_to = 0;
|
||||
|
||||
children = (gpointer *) value->contents.tree.children;
|
||||
n_children = value->contents.tree.n_children;
|
||||
@@ -464,10 +485,12 @@ g_variant_fill_gvs (GVariantSerialised *serialised,
|
||||
if (value->state & STATE_SERIALISED)
|
||||
{
|
||||
serialised->ordered_offsets_up_to = value->contents.serialised.ordered_offsets_up_to;
|
||||
+ serialised->checked_offsets_up_to = value->contents.serialised.checked_offsets_up_to;
|
||||
}
|
||||
else
|
||||
{
|
||||
serialised->ordered_offsets_up_to = 0;
|
||||
+ serialised->checked_offsets_up_to = 0;
|
||||
}
|
||||
|
||||
if (serialised->data)
|
||||
@@ -513,6 +536,7 @@ g_variant_ensure_serialised (GVariant *value)
|
||||
value->contents.serialised.data = g_bytes_get_data (bytes, NULL);
|
||||
value->contents.serialised.bytes = bytes;
|
||||
value->contents.serialised.ordered_offsets_up_to = G_MAXSIZE;
|
||||
+ value->contents.serialised.checked_offsets_up_to = G_MAXSIZE;
|
||||
value->state |= STATE_SERIALISED;
|
||||
}
|
||||
}
|
||||
@@ -594,6 +618,7 @@ g_variant_new_from_bytes (const GVariantType *type,
|
||||
serialised.data = (guchar *) g_bytes_get_data (bytes, &serialised.size);
|
||||
serialised.depth = 0;
|
||||
serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
|
||||
+ serialised.checked_offsets_up_to = trusted ? G_MAXSIZE : 0;
|
||||
|
||||
if (!g_variant_serialised_check (serialised))
|
||||
{
|
||||
@@ -644,6 +669,7 @@ g_variant_new_from_bytes (const GVariantType *type,
|
||||
}
|
||||
|
||||
value->contents.serialised.ordered_offsets_up_to = trusted ? G_MAXSIZE : 0;
|
||||
+ value->contents.serialised.checked_offsets_up_to = trusted ? G_MAXSIZE : 0;
|
||||
|
||||
g_clear_pointer (&owned_bytes, g_bytes_unref);
|
||||
|
||||
@@ -1120,6 +1146,7 @@ g_variant_get_child_value (GVariant *value,
|
||||
|
||||
/* Update the cached ordered_offsets_up_to, since @serialised will be thrown away when this function exits */
|
||||
value->contents.serialised.ordered_offsets_up_to = MAX (value->contents.serialised.ordered_offsets_up_to, serialised.ordered_offsets_up_to);
|
||||
+ value->contents.serialised.checked_offsets_up_to = MAX (value->contents.serialised.checked_offsets_up_to, serialised.checked_offsets_up_to);
|
||||
|
||||
/* Check whether this would cause nesting too deep. If so, return a fake
|
||||
* child. The only situation we expect this to happen in is with a variant,
|
||||
@@ -1147,6 +1174,7 @@ g_variant_get_child_value (GVariant *value,
|
||||
g_bytes_ref (value->contents.serialised.bytes);
|
||||
child->contents.serialised.data = s_child.data;
|
||||
child->contents.serialised.ordered_offsets_up_to = s_child.ordered_offsets_up_to;
|
||||
+ child->contents.serialised.checked_offsets_up_to = s_child.checked_offsets_up_to;
|
||||
|
||||
return child;
|
||||
}
|
||||
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
|
||||
index cd4a3e6..0bf7243 100644
|
||||
--- a/glib/gvariant-serialiser.c
|
||||
+++ b/glib/gvariant-serialiser.c
|
||||
@@ -120,6 +120,8 @@
|
||||
*
|
||||
* @depth has no restrictions; the depth of a top-level serialised #GVariant is
|
||||
* zero, and it increases for each level of nested child.
|
||||
+ *
|
||||
+ * @checked_offsets_up_to is always ≥ @ordered_offsets_up_to
|
||||
*/
|
||||
|
||||
/* < private >
|
||||
@@ -147,6 +149,9 @@ g_variant_serialised_check (GVariantSerialised serialised)
|
||||
!(serialised.size == 0 || serialised.data != NULL))
|
||||
return FALSE;
|
||||
|
||||
+ if (serialised.ordered_offsets_up_to > serialised.checked_offsets_up_to)
|
||||
+ return FALSE;
|
||||
+
|
||||
/* Depending on the native alignment requirements of the machine, the
|
||||
* compiler will insert either 3 or 7 padding bytes after the char.
|
||||
* This will result in the sizeof() the struct being 12 or 16.
|
||||
@@ -266,6 +271,7 @@ gvs_fixed_sized_maybe_get_child (GVariantSerialised value,
|
||||
g_variant_type_info_ref (value.type_info);
|
||||
value.depth++;
|
||||
value.ordered_offsets_up_to = 0;
|
||||
+ value.checked_offsets_up_to = 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -297,7 +303,7 @@ gvs_fixed_sized_maybe_serialise (GVariantSerialised value,
|
||||
{
|
||||
if (n_children)
|
||||
{
|
||||
- GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0 };
|
||||
+ GVariantSerialised child = { NULL, value.data, value.size, value.depth + 1, 0, 0 };
|
||||
|
||||
gvs_filler (&child, children[0]);
|
||||
}
|
||||
@@ -320,6 +326,7 @@ gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
|
||||
value.type_info = g_variant_type_info_element (value.type_info);
|
||||
value.depth++;
|
||||
value.ordered_offsets_up_to = 0;
|
||||
+ value.checked_offsets_up_to = 0;
|
||||
|
||||
return g_variant_serialised_is_normal (value);
|
||||
}
|
||||
@@ -362,6 +369,7 @@ gvs_variable_sized_maybe_get_child (GVariantSerialised value,
|
||||
|
||||
value.depth++;
|
||||
value.ordered_offsets_up_to = 0;
|
||||
+ value.checked_offsets_up_to = 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
@@ -392,7 +400,7 @@ gvs_variable_sized_maybe_serialise (GVariantSerialised value,
|
||||
{
|
||||
if (n_children)
|
||||
{
|
||||
- GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0 };
|
||||
+ GVariantSerialised child = { NULL, value.data, value.size - 1, value.depth + 1, 0, 0 };
|
||||
|
||||
/* write the data for the child. */
|
||||
gvs_filler (&child, children[0]);
|
||||
@@ -413,6 +421,7 @@ gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
|
||||
value.size--;
|
||||
value.depth++;
|
||||
value.ordered_offsets_up_to = 0;
|
||||
+ value.checked_offsets_up_to = 0;
|
||||
|
||||
return g_variant_serialised_is_normal (value);
|
||||
}
|
||||
@@ -739,39 +748,46 @@ gvs_variable_sized_array_get_child (GVariantSerialised value,
|
||||
|
||||
/* If the requested @index_ is beyond the set of indices whose framing offsets
|
||||
* have been checked, check the remaining offsets to see whether they’re
|
||||
- * normal (in order, no overlapping array elements). */
|
||||
- if (index_ > value.ordered_offsets_up_to)
|
||||
+ * normal (in order, no overlapping array elements).
|
||||
+ *
|
||||
+ * Don’t bother checking if the highest known-good offset is lower than the
|
||||
+ * highest checked offset, as that means there’s an invalid element at that
|
||||
+ * index, so there’s no need to check further. */
|
||||
+ if (index_ > value.checked_offsets_up_to &&
|
||||
+ value.ordered_offsets_up_to == value.checked_offsets_up_to)
|
||||
{
|
||||
switch (offsets.offset_size)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
value.ordered_offsets_up_to = find_unordered_guint8 (
|
||||
- offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ offsets.array, value.checked_offsets_up_to, index_ + 1);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
value.ordered_offsets_up_to = find_unordered_guint16 (
|
||||
- offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ offsets.array, value.checked_offsets_up_to, index_ + 1);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
value.ordered_offsets_up_to = find_unordered_guint32 (
|
||||
- offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ offsets.array, value.checked_offsets_up_to, index_ + 1);
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
value.ordered_offsets_up_to = find_unordered_guint64 (
|
||||
- offsets.array, value.ordered_offsets_up_to, index_ + 1);
|
||||
+ offsets.array, value.checked_offsets_up_to, index_ + 1);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* gvs_get_offset_size() only returns maximum 8 */
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
+
|
||||
+ value.checked_offsets_up_to = index_;
|
||||
}
|
||||
|
||||
if (index_ > value.ordered_offsets_up_to)
|
||||
@@ -916,6 +932,7 @@ gvs_variable_sized_array_is_normal (GVariantSerialised value)
|
||||
|
||||
/* All offsets have now been checked. */
|
||||
value.ordered_offsets_up_to = G_MAXSIZE;
|
||||
+ value.checked_offsets_up_to = G_MAXSIZE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@@ -1040,14 +1057,15 @@ gvs_tuple_get_child (GVariantSerialised value,
|
||||
* all the tuple *elements* here, not just all the framing offsets, since
|
||||
* tuples contain a mix of elements which use framing offsets and ones which
|
||||
* don’t. None of them are allowed to overlap. */
|
||||
- if (index_ > value.ordered_offsets_up_to)
|
||||
+ if (index_ > value.checked_offsets_up_to &&
|
||||
+ value.ordered_offsets_up_to == value.checked_offsets_up_to)
|
||||
{
|
||||
gsize i, prev_i_end = 0;
|
||||
|
||||
- if (value.ordered_offsets_up_to > 0)
|
||||
- gvs_tuple_get_member_bounds (value, value.ordered_offsets_up_to - 1, offset_size, NULL, &prev_i_end);
|
||||
+ if (value.checked_offsets_up_to > 0)
|
||||
+ gvs_tuple_get_member_bounds (value, value.checked_offsets_up_to - 1, offset_size, NULL, &prev_i_end);
|
||||
|
||||
- for (i = value.ordered_offsets_up_to; i <= index_; i++)
|
||||
+ for (i = value.checked_offsets_up_to; i <= index_; i++)
|
||||
{
|
||||
gsize i_start, i_end;
|
||||
|
||||
@@ -1060,6 +1078,7 @@ gvs_tuple_get_child (GVariantSerialised value,
|
||||
}
|
||||
|
||||
value.ordered_offsets_up_to = i - 1;
|
||||
+ value.checked_offsets_up_to = index_;
|
||||
}
|
||||
|
||||
if (index_ > value.ordered_offsets_up_to)
|
||||
@@ -1257,6 +1276,7 @@ gvs_tuple_is_normal (GVariantSerialised value)
|
||||
|
||||
/* All element bounds have been checked above. */
|
||||
value.ordered_offsets_up_to = G_MAXSIZE;
|
||||
+ value.checked_offsets_up_to = G_MAXSIZE;
|
||||
|
||||
{
|
||||
gsize fixed_size;
|
||||
diff --git a/glib/gvariant-serialiser.h b/glib/gvariant-serialiser.h
|
||||
index 144aec8..e132451 100644
|
||||
--- a/glib/gvariant-serialiser.h
|
||||
+++ b/glib/gvariant-serialiser.h
|
||||
@@ -40,6 +40,15 @@ typedef struct
|
||||
* Even when dealing with tuples, @ordered_offsets_up_to is an element index,
|
||||
* rather than an index into the frame offsets. */
|
||||
gsize ordered_offsets_up_to;
|
||||
+
|
||||
+ /* Similar to @ordered_offsets_up_to. This gives the index of the child element
|
||||
+ * whose frame offset is the highest in the offset table which has been
|
||||
+ * checked so far.
|
||||
+ *
|
||||
+ * This is always ≥ @ordered_offsets_up_to. It is always an element index.
|
||||
+ *
|
||||
+ * See documentation in gvariant-core.c for `struct GVariant` for details. */
|
||||
+ gsize checked_offsets_up_to;
|
||||
} GVariantSerialised;
|
||||
|
||||
/* deserialisation */
|
||||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||||
index fdd36be..f910bd4 100644
|
||||
--- a/glib/gvariant.c
|
||||
+++ b/glib/gvariant.c
|
||||
@@ -5945,6 +5945,7 @@ g_variant_byteswap (GVariant *value)
|
||||
serialised.size = g_variant_get_size (trusted);
|
||||
serialised.data = g_malloc (serialised.size);
|
||||
serialised.ordered_offsets_up_to = G_MAXSIZE; /* operating on the normal form */
|
||||
+ serialised.checked_offsets_up_to = G_MAXSIZE;
|
||||
g_variant_store (trusted, serialised.data);
|
||||
g_variant_unref (trusted);
|
||||
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index a84b02e..640f3c0 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -1286,6 +1286,7 @@ random_instance_filler (GVariantSerialised *serialised,
|
||||
|
||||
serialised->depth = 0;
|
||||
serialised->ordered_offsets_up_to = 0;
|
||||
+ serialised->checked_offsets_up_to = 0;
|
||||
|
||||
g_assert_true (serialised->type_info == instance->type_info);
|
||||
g_assert_cmpuint (serialised->size, ==, instance->size);
|
||||
@@ -1453,6 +1454,7 @@ test_maybe (void)
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
serialised.ordered_offsets_up_to = 0;
|
||||
+ serialised.checked_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised,
|
||||
random_instance_filler,
|
||||
@@ -1577,6 +1579,7 @@ test_array (void)
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
serialised.ordered_offsets_up_to = 0;
|
||||
+ serialised.checked_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||||
(gpointer *) instances, n_children);
|
||||
@@ -1742,6 +1745,7 @@ test_tuple (void)
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
serialised.ordered_offsets_up_to = 0;
|
||||
+ serialised.checked_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||||
(gpointer *) instances, n_children);
|
||||
@@ -1839,6 +1843,7 @@ test_variant (void)
|
||||
serialised.size = needed_size;
|
||||
serialised.depth = 0;
|
||||
serialised.ordered_offsets_up_to = 0;
|
||||
+ serialised.checked_offsets_up_to = 0;
|
||||
|
||||
g_variant_serialiser_serialise (serialised, random_instance_filler,
|
||||
(gpointer *) &instance, 1);
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
From 298a537d5f6783e55d87e40011ee3fd3b22b72f9 Mon Sep 17 00:00:00 2001
|
||||
From: Philip Withnall <pwithnall@endlessos.org>
|
||||
Date: Thu, 17 Aug 2023 01:39:01 +0000
|
||||
Subject: [PATCH] gvariant: Zero-initialise various GVariantSerialised objects
|
||||
|
||||
The following few commits will add a couple of new fields to
|
||||
`GVariantSerialised`, and they should be zero-filled by default.
|
||||
|
||||
Try and pre-empt that a bit by zero-filling `GVariantSerialised` by
|
||||
default in a few places.
|
||||
|
||||
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
|
||||
|
||||
Helps: #2121
|
||||
|
||||
CVE: CVE-2023-32665
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/glib/-/commit/298a537d5f6783e55d87e40011ee3fd3b22b72f9]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
glib/gvariant.c | 2 +-
|
||||
glib/tests/gvariant.c | 12 ++++++------
|
||||
2 files changed, 7 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/glib/gvariant.c b/glib/gvariant.c
|
||||
index f910bd4..8ba701e 100644
|
||||
--- a/glib/gvariant.c
|
||||
+++ b/glib/gvariant.c
|
||||
@@ -5936,7 +5936,7 @@ g_variant_byteswap (GVariant *value)
|
||||
if (alignment)
|
||||
/* (potentially) contains multi-byte numeric data */
|
||||
{
|
||||
- GVariantSerialised serialised;
|
||||
+ GVariantSerialised serialised = { 0, };
|
||||
GVariant *trusted;
|
||||
GBytes *bytes;
|
||||
|
||||
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
|
||||
index 640f3c0..d640c81 100644
|
||||
--- a/glib/tests/gvariant.c
|
||||
+++ b/glib/tests/gvariant.c
|
||||
@@ -1446,7 +1446,7 @@ test_maybe (void)
|
||||
|
||||
for (flavour = 0; flavour < 8; flavour += alignment)
|
||||
{
|
||||
- GVariantSerialised serialised;
|
||||
+ GVariantSerialised serialised = { 0, };
|
||||
GVariantSerialised child;
|
||||
|
||||
serialised.type_info = type_info;
|
||||
@@ -1572,7 +1572,7 @@ test_array (void)
|
||||
|
||||
for (flavour = 0; flavour < 8; flavour += alignment)
|
||||
{
|
||||
- GVariantSerialised serialised;
|
||||
+ GVariantSerialised serialised = { 0, };
|
||||
|
||||
serialised.type_info = array_info;
|
||||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||||
@@ -1738,7 +1738,7 @@ test_tuple (void)
|
||||
|
||||
for (flavour = 0; flavour < 8; flavour += alignment)
|
||||
{
|
||||
- GVariantSerialised serialised;
|
||||
+ GVariantSerialised serialised = { 0, };
|
||||
|
||||
serialised.type_info = type_info;
|
||||
serialised.data = flavoured_malloc (needed_size, flavour);
|
||||
@@ -1835,7 +1835,7 @@ test_variant (void)
|
||||
|
||||
for (flavour = 0; flavour < 8; flavour += alignment)
|
||||
{
|
||||
- GVariantSerialised serialised;
|
||||
+ GVariantSerialised serialised = { 0, };
|
||||
GVariantSerialised child;
|
||||
|
||||
serialised.type_info = type_info;
|
||||
@@ -2284,7 +2284,7 @@ serialise_tree (TreeInstance *tree,
|
||||
static void
|
||||
test_byteswap (void)
|
||||
{
|
||||
- GVariantSerialised one, two;
|
||||
+ GVariantSerialised one = { 0, }, two = { 0, };
|
||||
TreeInstance *tree;
|
||||
|
||||
tree = tree_instance_new (NULL, 3);
|
||||
@@ -2358,7 +2358,7 @@ test_serialiser_children (void)
|
||||
static void
|
||||
test_fuzz (gdouble *fuzziness)
|
||||
{
|
||||
- GVariantSerialised serialised;
|
||||
+ GVariantSerialised serialised = { 0, };
|
||||
TreeInstance *tree;
|
||||
|
||||
/* make an instance */
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -42,6 +42,20 @@ SRC_URI = "${GNOME_MIRROR}/glib/${SHRT_VER}/glib-${PV}.tar.xz \
|
||||
file://CVE-2021-28153-3.patch \
|
||||
file://CVE-2021-28153-4.patch \
|
||||
file://CVE-2021-28153-5.patch \
|
||||
file://CVE-2023-32665-0001.patch \
|
||||
file://CVE-2023-32665-0002.patch \
|
||||
file://CVE-2023-32665-0003.patch \
|
||||
file://CVE-2023-32665-0004.patch \
|
||||
file://CVE-2023-32665-0005.patch \
|
||||
file://CVE-2023-32665-0006.patch \
|
||||
file://CVE-2023-32665-0007.patch \
|
||||
file://CVE-2023-32665-0008.patch \
|
||||
file://CVE-2023-32665-0009.patch \
|
||||
file://CVE-2023-29499.patch \
|
||||
file://CVE-2023-32611-0001.patch \
|
||||
file://CVE-2023-32611-0002.patch \
|
||||
file://CVE-2023-32643.patch \
|
||||
file://CVE-2023-32636.patch \
|
||||
"
|
||||
|
||||
SRC_URI_append_class-native = " file://relocate-modules.patch"
|
||||
|
||||
63
meta/recipes-core/glibc/glibc/CVE-2023-4911.patch
Normal file
63
meta/recipes-core/glibc/glibc/CVE-2023-4911.patch
Normal file
@@ -0,0 +1,63 @@
|
||||
From d2b77337f734fcacdfc8e0ddec14cf31a746c7be Mon Sep 17 00:00:00 2001
|
||||
From: Siddhesh Poyarekar <siddhesh@redhat.com>
|
||||
Date: Mon, 11 Sep 2023 18:53:15 -0400
|
||||
Subject: [PATCH v2] tunables: Terminate immediately if end of input is reached
|
||||
|
||||
The string parsing routine may end up writing beyond bounds of tunestr
|
||||
if the input tunable string is malformed, of the form name=name=val.
|
||||
This gets processed twice, first as name=name=val and next as name=val,
|
||||
resulting in tunestr being name=name=val:name=val, thus overflowing
|
||||
tunestr.
|
||||
|
||||
Terminate the parsing loop at the first instance itself so that tunestr
|
||||
does not overflow.
|
||||
---
|
||||
Changes from v1:
|
||||
|
||||
- Also null-terminate tunestr before exiting.
|
||||
|
||||
elf/dl-tunables.c | 17 ++++++++++-------
|
||||
1 file changed, 10 insertions(+), 7 deletions(-)
|
||||
|
||||
Upstream-Status: Backport [git://sourceware.org/git/glibc.git]
|
||||
CVE: CVE-2023-4911
|
||||
|
||||
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
|
||||
index 8e7ee9df10..76cf8b9da3 100644
|
||||
--- a/elf/dl-tunables.c
|
||||
+++ b/elf/dl-tunables.c
|
||||
@@ -187,11 +187,7 @@ parse_tunables (char *tunestr, char *valstring)
|
||||
/* If we reach the end of the string before getting a valid name-value
|
||||
pair, bail out. */
|
||||
if (p[len] == '\0')
|
||||
- {
|
||||
- if (__libc_enable_secure)
|
||||
- tunestr[off] = '\0';
|
||||
- return;
|
||||
- }
|
||||
+ break;
|
||||
|
||||
/* We did not find a valid name-value pair before encountering the
|
||||
colon. */
|
||||
@@ -251,9 +247,16 @@ parse_tunables (char *tunestr, char *valstring)
|
||||
}
|
||||
}
|
||||
|
||||
- if (p[len] != '\0')
|
||||
- p += len + 1;
|
||||
+ /* We reached the end while processing the tunable string. */
|
||||
+ if (p[len] == '\0')
|
||||
+ break;
|
||||
+
|
||||
+ p += len + 1;
|
||||
}
|
||||
+
|
||||
+ /* Terminate tunestr before we leave. */
|
||||
+ if (__libc_enable_secure)
|
||||
+ tunestr[off] = '\0';
|
||||
}
|
||||
#endif
|
||||
|
||||
--
|
||||
2.41.0
|
||||
|
||||
@@ -29,6 +29,13 @@ CVE_CHECK_WHITELIST += "CVE-2019-1010025"
|
||||
# https://git.yoctoproject.org/cgit/cgit.cgi/poky/commit/?h=dunfell&id=e1e89ff7d75c3d2223f9e3bd875b9b0c5e15836b
|
||||
CVE_CHECK_WHITELIST += "CVE-2021-35942"
|
||||
|
||||
# glibc https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2023-4527
|
||||
# This vulnerability was introduced in 2.36 by commit
|
||||
# f282cdbe7f436c75864e5640a409a10485e9abb2 resolv: Implement no-aaaa stub resolver option
|
||||
# so our version is not yet vulnerable
|
||||
# See https://sourceware.org/bugzilla/show_bug.cgi?id=30842
|
||||
CVE_CHECK_WHITELIST += "CVE-2023-4527"
|
||||
|
||||
DEPENDS += "gperf-native bison-native make-native"
|
||||
|
||||
NATIVESDKFIXES ?= ""
|
||||
@@ -80,6 +87,7 @@ SRC_URI = "${GLIBC_GIT_URI};branch=${SRCBRANCH};name=glibc \
|
||||
file://0036-i386-Avoid-lazy-relocation-of-tlsdesc-BZ-27137.patch \
|
||||
file://0037-Avoid-deadlock-between-pthread_create-and-ctors.patch \
|
||||
file://CVE-2023-0687.patch \
|
||||
file://CVE-2023-4911.patch \
|
||||
"
|
||||
S = "${WORKDIR}/git"
|
||||
B = "${WORKDIR}/build-${TARGET_SYS}"
|
||||
|
||||
@@ -24,7 +24,7 @@ IMAGE_FSTYPES = "wic.vmdk"
|
||||
|
||||
inherit core-image setuptools3
|
||||
|
||||
SRCREV ?= "6d6d43248e003895aa02103b2d239238e97d6167"
|
||||
SRCREV ?= "b86bf0103c5d5ee04012473b80353e3da1f9e67f"
|
||||
SRC_URI = "git://git.yoctoproject.org/poky;branch=dunfell \
|
||||
file://Yocto_Build_Appliance.vmx \
|
||||
file://Yocto_Build_Appliance.vmxf \
|
||||
|
||||
36
meta/recipes-core/libxml/libxml2/CVE-2023-39615-0001.patch
Normal file
36
meta/recipes-core/libxml/libxml2/CVE-2023-39615-0001.patch
Normal file
@@ -0,0 +1,36 @@
|
||||
From d0c3f01e110d54415611c5fa0040cdf4a56053f9 Mon Sep 17 00:00:00 2001
|
||||
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
||||
Date: Sat, 6 May 2023 17:47:37 +0200
|
||||
Subject: [PATCH] parser: Fix old SAX1 parser with custom callbacks
|
||||
|
||||
For some reason, xmlCtxtUseOptionsInternal set the start and end element
|
||||
SAX handlers to the internal DOM builder functions when XML_PARSE_SAX1
|
||||
was specified. This means that custom SAX handlers could never work with
|
||||
that flag because these functions would receive the wrong user data
|
||||
argument and crash immediately.
|
||||
|
||||
Fixes #535.
|
||||
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/libxml2/-/commit/d0c3f01e110d54415611c5fa0040cdf4a56053f9]
|
||||
CVE: CVE-2023-39615
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
parser.c | 2 --
|
||||
1 file changed, 2 deletions(-)
|
||||
|
||||
diff --git a/parser.c b/parser.c
|
||||
index 6e09208..7814e6e 100644
|
||||
--- a/parser.c
|
||||
+++ b/parser.c
|
||||
@@ -15156,8 +15156,6 @@ xmlCtxtUseOptionsInternal(xmlParserCtxtPtr ctxt, int options, const char *encodi
|
||||
}
|
||||
#ifdef LIBXML_SAX1_ENABLED
|
||||
if (options & XML_PARSE_SAX1) {
|
||||
- ctxt->sax->startElement = xmlSAX2StartElement;
|
||||
- ctxt->sax->endElement = xmlSAX2EndElement;
|
||||
ctxt->sax->startElementNs = NULL;
|
||||
ctxt->sax->endElementNs = NULL;
|
||||
ctxt->sax->initialized = 1;
|
||||
--
|
||||
2.24.4
|
||||
|
||||
71
meta/recipes-core/libxml/libxml2/CVE-2023-39615-0002.patch
Normal file
71
meta/recipes-core/libxml/libxml2/CVE-2023-39615-0002.patch
Normal file
@@ -0,0 +1,71 @@
|
||||
From 235b15a590eecf97b09e87bdb7e4f8333e9de129 Mon Sep 17 00:00:00 2001
|
||||
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
||||
Date: Mon, 8 May 2023 17:58:02 +0200
|
||||
Subject: [PATCH] SAX: Always initialize SAX1 element handlers
|
||||
|
||||
Follow-up to commit d0c3f01e. A parser context will be initialized to
|
||||
SAX version 2, but this can be overridden with XML_PARSE_SAX1 later,
|
||||
so we must initialize the SAX1 element handlers as well.
|
||||
|
||||
Change the check in xmlDetectSAX2 to only look for XML_SAX2_MAGIC, so
|
||||
we don't switch to SAX1 if the SAX2 element handlers are NULL.
|
||||
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/libxml2/-/commit/235b15a590eecf97b09e87bdb7e4f8333e9de129]
|
||||
CVE: CVE-2023-39615
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
SAX2.c | 11 +++++++----
|
||||
parser.c | 5 +----
|
||||
2 files changed, 8 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/SAX2.c b/SAX2.c
|
||||
index 5f141f9..902d34d 100644
|
||||
--- a/SAX2.c
|
||||
+++ b/SAX2.c
|
||||
@@ -2869,20 +2869,23 @@ xmlSAXVersion(xmlSAXHandler *hdlr, int version)
|
||||
{
|
||||
if (hdlr == NULL) return(-1);
|
||||
if (version == 2) {
|
||||
- hdlr->startElement = NULL;
|
||||
- hdlr->endElement = NULL;
|
||||
hdlr->startElementNs = xmlSAX2StartElementNs;
|
||||
hdlr->endElementNs = xmlSAX2EndElementNs;
|
||||
hdlr->serror = NULL;
|
||||
hdlr->initialized = XML_SAX2_MAGIC;
|
||||
#ifdef LIBXML_SAX1_ENABLED
|
||||
} else if (version == 1) {
|
||||
- hdlr->startElement = xmlSAX2StartElement;
|
||||
- hdlr->endElement = xmlSAX2EndElement;
|
||||
hdlr->initialized = 1;
|
||||
#endif /* LIBXML_SAX1_ENABLED */
|
||||
} else
|
||||
return(-1);
|
||||
+#ifdef LIBXML_SAX1_ENABLED
|
||||
+ hdlr->startElement = xmlSAX2StartElement;
|
||||
+ hdlr->endElement = xmlSAX2EndElement;
|
||||
+#else
|
||||
+ hdlr->startElement = NULL;
|
||||
+ hdlr->endElement = NULL;
|
||||
+#endif /* LIBXML_SAX1_ENABLED */
|
||||
hdlr->internalSubset = xmlSAX2InternalSubset;
|
||||
hdlr->externalSubset = xmlSAX2ExternalSubset;
|
||||
hdlr->isStandalone = xmlSAX2IsStandalone;
|
||||
diff --git a/parser.c b/parser.c
|
||||
index 7814e6e..cf0fb38 100644
|
||||
--- a/parser.c
|
||||
+++ b/parser.c
|
||||
@@ -1102,10 +1102,7 @@ xmlDetectSAX2(xmlParserCtxtPtr ctxt) {
|
||||
if (ctxt == NULL) return;
|
||||
sax = ctxt->sax;
|
||||
#ifdef LIBXML_SAX1_ENABLED
|
||||
- if ((sax) && (sax->initialized == XML_SAX2_MAGIC) &&
|
||||
- ((sax->startElementNs != NULL) ||
|
||||
- (sax->endElementNs != NULL) ||
|
||||
- ((sax->startElement == NULL) && (sax->endElement == NULL))))
|
||||
+ if ((sax) && (sax->initialized == XML_SAX2_MAGIC))
|
||||
ctxt->sax2 = 1;
|
||||
#else
|
||||
ctxt->sax2 = 1;
|
||||
--
|
||||
2.24.4
|
||||
|
||||
44
meta/recipes-core/libxml/libxml2/CVE-2023-39615-pre.patch
Normal file
44
meta/recipes-core/libxml/libxml2/CVE-2023-39615-pre.patch
Normal file
@@ -0,0 +1,44 @@
|
||||
From 99fc048d7f7292c5ee18e44c400bd73bc63a47ed Mon Sep 17 00:00:00 2001
|
||||
From: Nick Wellnhofer <wellnhofer@aevum.de>
|
||||
Date: Fri, 14 Aug 2020 14:18:50 +0200
|
||||
Subject: [PATCH] Don't use SAX1 if all element handlers are NULL
|
||||
|
||||
Running xmllint with "--sax --noout" installs a SAX2 handler with all
|
||||
callbacks set to NULL. In this case or similar situations, we don't want
|
||||
to switch to SAX1 parsing.
|
||||
|
||||
Note: This patch is needed for "CVE-2023-39615-0002" patch to apply.
|
||||
Without this patch the build will fail with undefined sax error.
|
||||
|
||||
Upstream-Status: Backport from [https://gitlab.gnome.org/GNOME/libxml2/-/commit/99fc048d7f7292c5ee18e44c400bd73bc63a47ed]
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
parser.c | 10 +++++++---
|
||||
1 file changed, 7 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/parser.c b/parser.c
|
||||
index bb677b0..6e09208 100644
|
||||
--- a/parser.c
|
||||
+++ b/parser.c
|
||||
@@ -1098,11 +1098,15 @@ xmlHasFeature(xmlFeature feature)
|
||||
*/
|
||||
static void
|
||||
xmlDetectSAX2(xmlParserCtxtPtr ctxt) {
|
||||
+ xmlSAXHandlerPtr sax;
|
||||
if (ctxt == NULL) return;
|
||||
+ sax = ctxt->sax;
|
||||
#ifdef LIBXML_SAX1_ENABLED
|
||||
- if ((ctxt->sax) && (ctxt->sax->initialized == XML_SAX2_MAGIC) &&
|
||||
- ((ctxt->sax->startElementNs != NULL) ||
|
||||
- (ctxt->sax->endElementNs != NULL))) ctxt->sax2 = 1;
|
||||
+ if ((sax) && (sax->initialized == XML_SAX2_MAGIC) &&
|
||||
+ ((sax->startElementNs != NULL) ||
|
||||
+ (sax->endElementNs != NULL) ||
|
||||
+ ((sax->startElement == NULL) && (sax->endElement == NULL))))
|
||||
+ ctxt->sax2 = 1;
|
||||
#else
|
||||
ctxt->sax2 = 1;
|
||||
#endif /* LIBXML_SAX1_ENABLED */
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -38,6 +38,9 @@ SRC_URI += "http://www.w3.org/XML/Test/xmlts20080827.tar.gz;subdir=${BP};name=te
|
||||
file://CVE-2022-40304.patch \
|
||||
file://CVE-2023-28484.patch \
|
||||
file://CVE-2023-29469.patch \
|
||||
file://CVE-2023-39615-pre.patch \
|
||||
file://CVE-2023-39615-0001.patch \
|
||||
file://CVE-2023-39615-0002.patch \
|
||||
"
|
||||
|
||||
SRC_URI[archive.sha256sum] = "593b7b751dd18c2d6abcd0c4bcb29efc203d0b4373a6df98e3a455ea74ae2813"
|
||||
|
||||
120
meta/recipes-core/systemd/systemd/CVE-2018-21029.patch
Normal file
120
meta/recipes-core/systemd/systemd/CVE-2018-21029.patch
Normal file
@@ -0,0 +1,120 @@
|
||||
From 3f9d9289ee8730a81a0464539f4e1ba2d23d0ce9 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= <joerg@thalheim.io>
|
||||
Date: Tue, 3 Mar 2020 23:31:25 +0000
|
||||
Subject: [PATCH] systemd-resolved: use hostname for certificate validation in
|
||||
DoT
|
||||
|
||||
Widely accepted certificates for IP addresses are expensive and only
|
||||
affordable for larger organizations. Therefore if the user provides
|
||||
the hostname in the DNS= option, we should use it instead of the IP
|
||||
address.
|
||||
|
||||
(cherry picked from commit eec394f10bbfcc3d2fc8504ad8ff5be44231abd5)
|
||||
|
||||
CVE: CVE-2018-21029
|
||||
Upstream-Status: Backport [ff26d281aec0877b43269f18c6282cd79a7f5529]
|
||||
Signed-off-by: Marek Vasut <marex@denx.de>
|
||||
---
|
||||
man/resolved.conf.xml | 16 +++++++++++-----
|
||||
src/resolve/resolved-dnstls-gnutls.c | 20 ++++++++++++--------
|
||||
src/resolve/resolved-dnstls-openssl.c | 15 +++++++++++----
|
||||
3 files changed, 34 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml
|
||||
index 818000145b..37161ebcbc 100644
|
||||
--- a/man/resolved.conf.xml
|
||||
+++ b/man/resolved.conf.xml
|
||||
@@ -193,11 +193,17 @@
|
||||
<varlistentry>
|
||||
<term><varname>DNSOverTLS=</varname></term>
|
||||
<listitem>
|
||||
- <para>Takes a boolean argument or <literal>opportunistic</literal>.
|
||||
- If true all connections to the server will be encrypted. Note that
|
||||
- this mode requires a DNS server that supports DNS-over-TLS and has
|
||||
- a valid certificate for it's IP. If the DNS server does not support
|
||||
- DNS-over-TLS all DNS requests will fail. When set to <literal>opportunistic</literal>
|
||||
+ <para>Takes a boolean argument or <literal>opportunistic</literal>. If
|
||||
+ true all connections to the server will be encrypted. Note that this
|
||||
+ mode requires a DNS server that supports DNS-over-TLS and has a valid
|
||||
+ certificate. If the hostname was specified in <varname>DNS=</varname>
|
||||
+ by using the format format <literal>address#server_name</literal> it
|
||||
+ is used to validate its certificate and also to enable Server Name
|
||||
+ Indication (SNI) when opening a TLS connection. Otherwise
|
||||
+ the certificate is checked against the server's IP.
|
||||
+ If the DNS server does not support DNS-over-TLS all DNS requests will fail.</para>
|
||||
+
|
||||
+ <para>When set to <literal>opportunistic</literal>
|
||||
DNS request are attempted to send encrypted with DNS-over-TLS.
|
||||
If the DNS server does not support TLS, DNS-over-TLS is disabled.
|
||||
Note that this mode makes DNS-over-TLS vulnerable to "downgrade"
|
||||
diff --git a/src/resolve/resolved-dnstls-gnutls.c b/src/resolve/resolved-dnstls-gnutls.c
|
||||
index ed0a31e8bf..c7215723a7 100644
|
||||
--- a/src/resolve/resolved-dnstls-gnutls.c
|
||||
+++ b/src/resolve/resolved-dnstls-gnutls.c
|
||||
@@ -56,15 +56,19 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
|
||||
}
|
||||
|
||||
if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
|
||||
- stream->dnstls_data.validation.type = GNUTLS_DT_IP_ADDRESS;
|
||||
- if (server->family == AF_INET) {
|
||||
- stream->dnstls_data.validation.data = (unsigned char*) &server->address.in.s_addr;
|
||||
- stream->dnstls_data.validation.size = 4;
|
||||
- } else {
|
||||
- stream->dnstls_data.validation.data = server->address.in6.s6_addr;
|
||||
- stream->dnstls_data.validation.size = 16;
|
||||
+ if (server->server_name)
|
||||
+ gnutls_session_set_verify_cert(gs, server->server_name, 0);
|
||||
+ else {
|
||||
+ stream->dnstls_data.validation.type = GNUTLS_DT_IP_ADDRESS;
|
||||
+ if (server->family == AF_INET) {
|
||||
+ stream->dnstls_data.validation.data = (unsigned char*) &server->address.in.s_addr;
|
||||
+ stream->dnstls_data.validation.size = 4;
|
||||
+ } else {
|
||||
+ stream->dnstls_data.validation.data = server->address.in6.s6_addr;
|
||||
+ stream->dnstls_data.validation.size = 16;
|
||||
+ }
|
||||
+ gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0);
|
||||
}
|
||||
- gnutls_session_set_verify_cert2(gs, &stream->dnstls_data.validation, 1, 0);
|
||||
}
|
||||
|
||||
gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
|
||||
diff --git a/src/resolve/resolved-dnstls-openssl.c b/src/resolve/resolved-dnstls-openssl.c
|
||||
index 85e202ff74..007aedaa5b 100644
|
||||
--- a/src/resolve/resolved-dnstls-openssl.c
|
||||
+++ b/src/resolve/resolved-dnstls-openssl.c
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
+#include <openssl/x509v3.h>
|
||||
|
||||
#include "io-util.h"
|
||||
#include "resolved-dns-stream.h"
|
||||
@@ -78,13 +79,19 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
|
||||
|
||||
if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
|
||||
X509_VERIFY_PARAM *v;
|
||||
- const unsigned char *ip;
|
||||
|
||||
SSL_set_verify(s, SSL_VERIFY_PEER, NULL);
|
||||
v = SSL_get0_param(s);
|
||||
- ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
|
||||
- if (!X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)))
|
||||
- return -ECONNREFUSED;
|
||||
+ if (server->server_name) {
|
||||
+ X509_VERIFY_PARAM_set_hostflags(v, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
|
||||
+ if (X509_VERIFY_PARAM_set1_host(v, server->server_name, 0) == 0)
|
||||
+ return -ECONNREFUSED;
|
||||
+ } else {
|
||||
+ const unsigned char *ip;
|
||||
+ ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
|
||||
+ if (X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)) == 0)
|
||||
+ return -ECONNREFUSED;
|
||||
+ }
|
||||
}
|
||||
|
||||
ERR_clear_error();
|
||||
--
|
||||
2.40.1
|
||||
|
||||
@@ -31,6 +31,7 @@ SRC_URI += "file://touchscreen.rules \
|
||||
file://network-fix-Link-reference-counter-issue.patch \
|
||||
file://rm-rf-refactor-rm-rf-children-split-out-body-of-directory.patch \
|
||||
file://rm-rf-optionally-fsync-after-removing-directory-tree.patch \
|
||||
file://CVE-2018-21029.patch \
|
||||
file://CVE-2021-3997-1.patch \
|
||||
file://CVE-2021-3997-2.patch \
|
||||
file://CVE-2021-3997-3.patch \
|
||||
|
||||
40
meta/recipes-core/zlib/zlib/CVE-2023-45853.patch
Normal file
40
meta/recipes-core/zlib/zlib/CVE-2023-45853.patch
Normal file
@@ -0,0 +1,40 @@
|
||||
From 73331a6a0481067628f065ffe87bb1d8f787d10c Mon Sep 17 00:00:00 2001
|
||||
From: Hans Wennborg <hans@chromium.org>
|
||||
Date: Fri, 18 Aug 2023 11:05:33 +0200
|
||||
Subject: [PATCH] Reject overflows of zip header fields in minizip.
|
||||
|
||||
This checks the lengths of the file name, extra field, and comment
|
||||
that would be put in the zip headers, and rejects them if they are
|
||||
too long. They are each limited to 65535 bytes in length by the zip
|
||||
format. This also avoids possible buffer overflows if the provided
|
||||
fields are too long.
|
||||
|
||||
Upstream-Status: Backport from [https://github.com/madler/zlib/commit/73331a6a0481067628f065ffe87bb1d8f787d10c]
|
||||
CVE: CVE-2023-45853
|
||||
Signed-off-by: Ashish Sharma <asharma@mvista.com>
|
||||
---
|
||||
contrib/minizip/zip.c | 11 +++++++++++
|
||||
1 file changed, 11 insertions(+)
|
||||
|
||||
diff --git a/contrib/minizip/zip.c b/contrib/minizip/zip.c
|
||||
index 3d3d4cadd..0446109b2 100644
|
||||
--- a/contrib/minizip/zip.c
|
||||
+++ b/contrib/minizip/zip.c
|
||||
@@ -1043,6 +1043,17 @@ extern int ZEXPORT zipOpenNewFileInZip4_64(zipFile file, const char* filename, c
|
||||
return ZIP_PARAMERROR;
|
||||
#endif
|
||||
|
||||
+ // The filename and comment length must fit in 16 bits.
|
||||
+ if ((filename!=NULL) && (strlen(filename)>0xffff))
|
||||
+ return ZIP_PARAMERROR;
|
||||
+ if ((comment!=NULL) && (strlen(comment)>0xffff))
|
||||
+ return ZIP_PARAMERROR;
|
||||
+ // The extra field length must fit in 16 bits. If the member also requires
|
||||
+ // a Zip64 extra block, that will also need to fit within that 16-bit
|
||||
+ // length, but that will be checked for later.
|
||||
+ if ((size_extrafield_local>0xffff) || (size_extrafield_global>0xffff))
|
||||
+ return ZIP_PARAMERROR;
|
||||
+
|
||||
zi = (zip64_internal*)file;
|
||||
|
||||
if (zi->in_opened_file_inzip == 1)
|
||||
@@ -11,6 +11,7 @@ SRC_URI = "${SOURCEFORGE_MIRROR}/libpng/${BPN}/${PV}/${BPN}-${PV}.tar.xz \
|
||||
file://CVE-2018-25032.patch \
|
||||
file://run-ptest \
|
||||
file://CVE-2022-37434.patch \
|
||||
file://CVE-2023-45853.patch \
|
||||
"
|
||||
UPSTREAM_CHECK_URI = "http://zlib.net/"
|
||||
|
||||
|
||||
@@ -53,5 +53,7 @@ SRC_URI = "\
|
||||
file://CVE-2020-16593.patch \
|
||||
file://0001-CVE-2021-45078.patch \
|
||||
file://CVE-2022-38533.patch \
|
||||
file://CVE-2023-25588.patch \
|
||||
file://CVE-2021-46174.patch \
|
||||
"
|
||||
S = "${WORKDIR}/git"
|
||||
|
||||
35
meta/recipes-devtools/binutils/binutils/CVE-2021-46174.patch
Normal file
35
meta/recipes-devtools/binutils/binutils/CVE-2021-46174.patch
Normal file
@@ -0,0 +1,35 @@
|
||||
From 46322722ad40ac1a75672ae0f62f4969195f1368 Mon Sep 17 00:00:00 2001
|
||||
From: Alan Modra <amodra@gmail.com>
|
||||
Date: Thu, 20 Jan 2022 13:58:38 +1030
|
||||
Subject: [PATCH] PR28753, buffer overflow in read_section_stabs_debugging_info
|
||||
|
||||
PR 28753
|
||||
* rddbg.c (read_section_stabs_debugging_info): Don't read past
|
||||
end of section when concatentating stab strings.
|
||||
|
||||
CVE: CVE-2021-46174
|
||||
Upstream-Status: Backport [https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=cad4d6b91e97]
|
||||
|
||||
(cherry picked from commit 085b299b71721e15f5c5c5344dc3e4e4536dadba)
|
||||
(cherry picked from commit cad4d6b91e97b6962807d33c04ed7e7797788438)
|
||||
Signed-off-by: poojitha adireddy <pooadire@cisco.com>
|
||||
---
|
||||
binutils/rddbg.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/binutils/rddbg.c b/binutils/rddbg.c
|
||||
index 72e934055b5..5e76d94a3c4 100644
|
||||
--- a/binutils/rddbg.c
|
||||
+++ b/binutils/rddbg.c
|
||||
@@ -207,7 +207,7 @@ read_section_stabs_debugging_info (bfd *abfd, asymbol **syms, long symcount,
|
||||
an attempt to read the byte before 'strings' would occur. */
|
||||
while ((len = strlen (s)) > 0
|
||||
&& s[len - 1] == '\\'
|
||||
- && stab + 12 < stabs + stabsize)
|
||||
+ && stab + 16 <= stabs + stabsize)
|
||||
{
|
||||
char *p;
|
||||
|
||||
--
|
||||
2.23.1
|
||||
|
||||
149
meta/recipes-devtools/binutils/binutils/CVE-2023-25588.patch
Normal file
149
meta/recipes-devtools/binutils/binutils/CVE-2023-25588.patch
Normal file
@@ -0,0 +1,149 @@
|
||||
From d12f8998d2d086f0a6606589e5aedb7147e6f2f1 Mon Sep 17 00:00:00 2001
|
||||
From: Alan Modra <amodra@gmail.com>
|
||||
Date: Fri, 14 Oct 2022 10:30:21 +1030
|
||||
Subject: [PATCH] PR29677, Field `the_bfd` of `asymbol` is uninitialised
|
||||
|
||||
Besides not initialising the_bfd of synthetic symbols, counting
|
||||
symbols when sizing didn't match symbols created if there were any
|
||||
dynsyms named "". We don't want synthetic symbols without names
|
||||
anyway, so get rid of them. Also, simplify and correct sanity checks.
|
||||
|
||||
PR 29677
|
||||
* mach-o.c (bfd_mach_o_get_synthetic_symtab): Rewrite.
|
||||
---
|
||||
Upstream-Status: Backport from [https://sourceware.org/git/?p=binutils-gdb.git;a=patch;h=d12f8998d2d086f0a6606589e5aedb7147e6f2f1]
|
||||
CVE: CVE-2023-25588
|
||||
CVE: CVE-2022-47696
|
||||
|
||||
Signed-off-by: Ashish Sharma <asharma@mvista.com>
|
||||
Signed-off-by: poojitha adireddy <pooadire@cisco.com>
|
||||
|
||||
bfd/mach-o.c | 72 ++++++++++++++++++++++------------------------------
|
||||
1 file changed, 31 insertions(+), 41 deletions(-)
|
||||
|
||||
diff --git a/bfd/mach-o.c b/bfd/mach-o.c
|
||||
index acb35e7f0c6..5279343768c 100644
|
||||
--- a/bfd/mach-o.c
|
||||
+++ b/bfd/mach-o.c
|
||||
@@ -938,11 +938,9 @@ bfd_mach_o_get_synthetic_symtab (bfd *abfd,
|
||||
bfd_mach_o_symtab_command *symtab = mdata->symtab;
|
||||
asymbol *s;
|
||||
char * s_start;
|
||||
- char * s_end;
|
||||
unsigned long count, i, j, n;
|
||||
size_t size;
|
||||
char *names;
|
||||
- char *nul_name;
|
||||
const char stub [] = "$stub";
|
||||
|
||||
*ret = NULL;
|
||||
@@ -955,27 +953,27 @@ bfd_mach_o_get_synthetic_symtab (bfd *abfd,
|
||||
/* We need to allocate a bfd symbol for every indirect symbol and to
|
||||
allocate the memory for its name. */
|
||||
count = dysymtab->nindirectsyms;
|
||||
- size = count * sizeof (asymbol) + 1;
|
||||
-
|
||||
+ size = 0;
|
||||
for (j = 0; j < count; j++)
|
||||
{
|
||||
- const char * strng;
|
||||
unsigned int isym = dysymtab->indirect_syms[j];
|
||||
+ const char *str;
|
||||
|
||||
/* Some indirect symbols are anonymous. */
|
||||
- if (isym < symtab->nsyms && (strng = symtab->symbols[isym].symbol.name))
|
||||
- /* PR 17512: file: f5b8eeba. */
|
||||
- size += strnlen (strng, symtab->strsize - (strng - symtab->strtab)) + sizeof (stub);
|
||||
+ if (isym < symtab->nsyms
|
||||
+ && (str = symtab->symbols[isym].symbol.name) != NULL)
|
||||
+ {
|
||||
+ /* PR 17512: file: f5b8eeba. */
|
||||
+ size += strnlen (str, symtab->strsize - (str - symtab->strtab));
|
||||
+ size += sizeof (stub);
|
||||
+ }
|
||||
}
|
||||
|
||||
- s_start = bfd_malloc (size);
|
||||
+ s_start = bfd_malloc (size + count * sizeof (asymbol));
|
||||
s = *ret = (asymbol *) s_start;
|
||||
if (s == NULL)
|
||||
return -1;
|
||||
names = (char *) (s + count);
|
||||
- nul_name = names;
|
||||
- *names++ = 0;
|
||||
- s_end = s_start + size;
|
||||
|
||||
n = 0;
|
||||
for (i = 0; i < mdata->nsects; i++)
|
||||
@@ -997,47 +995,39 @@ bfd_mach_o_get_synthetic_symtab (bfd *abfd,
|
||||
entry_size = bfd_mach_o_section_get_entry_size (abfd, sec);
|
||||
|
||||
/* PR 17512: file: 08e15eec. */
|
||||
- if (first >= count || last >= count || first > last)
|
||||
+ if (first >= count || last > count || first > last)
|
||||
goto fail;
|
||||
|
||||
for (j = first; j < last; j++)
|
||||
{
|
||||
unsigned int isym = dysymtab->indirect_syms[j];
|
||||
-
|
||||
- /* PR 17512: file: 04d64d9b. */
|
||||
- if (((char *) s) + sizeof (* s) > s_end)
|
||||
- goto fail;
|
||||
-
|
||||
- s->flags = BSF_GLOBAL | BSF_SYNTHETIC;
|
||||
- s->section = sec->bfdsection;
|
||||
- s->value = addr - sec->addr;
|
||||
- s->udata.p = NULL;
|
||||
+ const char *str;
|
||||
+ size_t len;
|
||||
|
||||
if (isym < symtab->nsyms
|
||||
- && symtab->symbols[isym].symbol.name)
|
||||
+ && (str = symtab->symbols[isym].symbol.name) != NULL)
|
||||
{
|
||||
- const char *sym = symtab->symbols[isym].symbol.name;
|
||||
- size_t len;
|
||||
-
|
||||
- s->name = names;
|
||||
- len = strlen (sym);
|
||||
- /* PR 17512: file: 47dfd4d2. */
|
||||
- if (names + len >= s_end)
|
||||
+ /* PR 17512: file: 04d64d9b. */
|
||||
+ if (n >= count)
|
||||
goto fail;
|
||||
- memcpy (names, sym, len);
|
||||
- names += len;
|
||||
- /* PR 17512: file: 18f340a4. */
|
||||
- if (names + sizeof (stub) >= s_end)
|
||||
+ len = strnlen (str, symtab->strsize - (str - symtab->strtab));
|
||||
+ /* PR 17512: file: 47dfd4d2, 18f340a4. */
|
||||
+ if (size < len + sizeof (stub))
|
||||
goto fail;
|
||||
- memcpy (names, stub, sizeof (stub));
|
||||
- names += sizeof (stub);
|
||||
+ memcpy (names, str, len);
|
||||
+ memcpy (names + len, stub, sizeof (stub));
|
||||
+ s->name = names;
|
||||
+ names += len + sizeof (stub);
|
||||
+ size -= len + sizeof (stub);
|
||||
+ s->the_bfd = symtab->symbols[isym].symbol.the_bfd;
|
||||
+ s->flags = BSF_GLOBAL | BSF_SYNTHETIC;
|
||||
+ s->section = sec->bfdsection;
|
||||
+ s->value = addr - sec->addr;
|
||||
+ s->udata.p = NULL;
|
||||
+ s++;
|
||||
+ n++;
|
||||
}
|
||||
- else
|
||||
- s->name = nul_name;
|
||||
-
|
||||
addr += entry_size;
|
||||
- s++;
|
||||
- n++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
--
|
||||
2.39.3
|
||||
|
||||
@@ -70,6 +70,7 @@ SRC_URI = "\
|
||||
file://0038-gentypes-genmodes-Do-not-use-__LINE__-for-maintainin.patch \
|
||||
file://0039-process_alt_operands-Don-t-match-user-defined-regs-o.patch \
|
||||
file://0002-libstdc-Fix-inconsistent-noexcept-specific-for-valar.patch \
|
||||
file://CVE-2023-4039.patch \
|
||||
"
|
||||
S = "${TMPDIR}/work-shared/gcc-${PV}-${PR}/gcc-${PV}"
|
||||
SRC_URI[sha256sum] = "27769f64ef1d4cd5e2be8682c0c93f9887983e6cfd1a927ce5a0a2915a95cf8f"
|
||||
|
||||
1506
meta/recipes-devtools/gcc/gcc-9.5/CVE-2023-4039.patch
Normal file
1506
meta/recipes-devtools/gcc/gcc-9.5/CVE-2023-4039.patch
Normal file
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@ SRC_URI = "${GNU_MIRROR}/gdb/gdb-${PV}.tar.xz \
|
||||
file://0009-resolve-restrict-keyword-conflict.patch \
|
||||
file://0010-Fix-invalid-sigprocmask-call.patch \
|
||||
file://0011-gdbserver-ctrl-c-handling.patch \
|
||||
file://0012-CVE-2023-39128.patch \
|
||||
"
|
||||
SRC_URI[md5sum] = "f7e9f6236c425097d9e5f18a6ac40655"
|
||||
SRC_URI[sha256sum] = "699e0ec832fdd2f21c8266171ea5bf44024bd05164fdf064e4d10cc4cf0d1737"
|
||||
|
||||
75
meta/recipes-devtools/gdb/gdb/0012-CVE-2023-39128.patch
Normal file
75
meta/recipes-devtools/gdb/gdb/0012-CVE-2023-39128.patch
Normal file
@@ -0,0 +1,75 @@
|
||||
From 033bc52bb6190393c8eed80925fa78cc35b40c6d Mon Sep 17 00:00:00 2001
|
||||
From: Tom Tromey <tromey@adacore.com>
|
||||
Date: Wed, 16 Aug 2023 11:29:19 -0600
|
||||
Subject: [PATCH] Avoid buffer overflow in ada_decode
|
||||
|
||||
A bug report pointed out a buffer overflow in ada_decode, which Keith
|
||||
helpfully analyzed. ada_decode had a logic error when the input was
|
||||
all digits. While this isn't valid -- and would probably only appear
|
||||
in fuzzer tests -- it still should be handled properly.
|
||||
|
||||
This patch adds a missing bounds check. Tested with the self-tests in
|
||||
an asan build.
|
||||
|
||||
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30639
|
||||
Reviewed-by: Keith Seitz <keiths@redhat.com>
|
||||
|
||||
Upstream-Status: Backport from [https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=033bc52bb6190393c8eed80925fa78cc35b40c6d]
|
||||
CVE: CVE-2023-39128
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
gdb/ada-lang.c | 19 ++++++++++++++++++-
|
||||
1 file changed, 18 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/gdb/ada-lang.c b/gdb/ada-lang.c
|
||||
index 0c2d4fc..40852b6 100644
|
||||
--- a/gdb/ada-lang.c
|
||||
+++ b/gdb/ada-lang.c
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "cli/cli-utils.h"
|
||||
#include "gdbsupport/function-view.h"
|
||||
#include "gdbsupport/byte-vector.h"
|
||||
+#include "gdbsupport/selftest.h"
|
||||
#include <algorithm>
|
||||
|
||||
/* Define whether or not the C operator '/' truncates towards zero for
|
||||
@@ -1184,7 +1185,7 @@ ada_decode (const char *encoded)
|
||||
i -= 1;
|
||||
if (i > 1 && encoded[i] == '_' && encoded[i - 1] == '_')
|
||||
len0 = i - 1;
|
||||
- else if (encoded[i] == '$')
|
||||
+ else if (i >= 0 && encoded[i] == '$')
|
||||
len0 = i;
|
||||
}
|
||||
|
||||
@@ -1350,6 +1351,18 @@ Suppress:
|
||||
|
||||
}
|
||||
|
||||
+#ifdef GDB_SELF_TEST
|
||||
+
|
||||
+static void
|
||||
+ada_decode_tests ()
|
||||
+{
|
||||
+ /* This isn't valid, but used to cause a crash. PR gdb/30639. The
|
||||
+ result does not really matter very much. */
|
||||
+ SELF_CHECK (ada_decode ("44") == "44");
|
||||
+}
|
||||
+
|
||||
+#endif
|
||||
+
|
||||
/* Table for keeping permanent unique copies of decoded names. Once
|
||||
allocated, names in this table are never released. While this is a
|
||||
storage leak, it should not be significant unless there are massive
|
||||
@@ -14345,4 +14358,8 @@ DWARF attribute."),
|
||||
gdb::observers::new_objfile.attach (ada_new_objfile_observer);
|
||||
gdb::observers::free_objfile.attach (ada_free_objfile_observer);
|
||||
gdb::observers::inferior_exit.attach (ada_inferior_exit);
|
||||
+
|
||||
+#ifdef GDB_SELF_TEST
|
||||
+ selftests::register_test ("ada-decode", ada_decode_tests);
|
||||
+#endif
|
||||
}
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -60,7 +60,10 @@ SRC_URI += "\
|
||||
file://CVE-2023-24534.patch \
|
||||
file://CVE-2023-24538-1.patch \
|
||||
file://CVE-2023-24538-2.patch \
|
||||
file://CVE-2023-24538-3.patch \
|
||||
file://CVE-2023-24538_3.patch \
|
||||
file://CVE-2023-24538_4.patch \
|
||||
file://CVE-2023-24538_5.patch \
|
||||
file://CVE-2023-24538_6.patch \
|
||||
file://CVE-2023-24539.patch \
|
||||
file://CVE-2023-24540.patch \
|
||||
file://CVE-2023-29405-1.patch \
|
||||
@@ -70,6 +73,15 @@ SRC_URI += "\
|
||||
file://CVE-2023-29400.patch \
|
||||
file://CVE-2023-29406.patch \
|
||||
file://CVE-2023-29409.patch \
|
||||
file://CVE-2022-41725-pre1.patch \
|
||||
file://CVE-2022-41725-pre2.patch \
|
||||
file://CVE-2022-41725-pre3.patch \
|
||||
file://CVE-2022-41725.patch \
|
||||
file://CVE-2023-24536_1.patch \
|
||||
file://CVE-2023-24536_2.patch \
|
||||
file://CVE-2023-24536_3.patch \
|
||||
file://CVE-2023-39318.patch \
|
||||
file://CVE-2023-39319.patch \
|
||||
"
|
||||
|
||||
SRC_URI_append_libc-musl = " file://0009-ld-replace-glibc-dynamic-linker-with-musl.patch"
|
||||
|
||||
85
meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre1.patch
Normal file
85
meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre1.patch
Normal file
@@ -0,0 +1,85 @@
|
||||
From 874b3132a84cf76da6a48978826c04c380a37a50 Mon Sep 17 00:00:00 2001
|
||||
From: avivklas <avivklas@gmail.com>
|
||||
Date: Fri, 7 Aug 2020 21:50:12 +0300
|
||||
Subject: [PATCH] mime/multipart: return overflow errors in Reader.ReadForm
|
||||
|
||||
Updates Reader.ReadForm to check for overflow errors that may
|
||||
result from a leeway addition of 10MiB to the input argument
|
||||
maxMemory.
|
||||
|
||||
Fixes #40430
|
||||
|
||||
Change-Id: I510b8966c95c51d04695ba9d08fcfe005fd11a5d
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/247477
|
||||
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
|
||||
Trust: Cuong Manh Le <cuong.manhle.vn@gmail.com>
|
||||
Trust: Emmanuel Odeke <emm.odeke@gmail.com>
|
||||
TryBot-Result: Go Bot <gobot@golang.org>
|
||||
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/874b3132a84cf76da6a48978826c04c380a37a50]
|
||||
CVE: CVE-2022-41725 #Dependency Patch1
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
src/mime/multipart/formdata.go | 4 ++++
|
||||
src/mime/multipart/formdata_test.go | 18 ++++++++++++++++++
|
||||
2 files changed, 22 insertions(+)
|
||||
|
||||
diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go
|
||||
index 832d0ad693666..4eb31012941ac 100644
|
||||
--- a/src/mime/multipart/formdata.go
|
||||
+++ b/src/mime/multipart/formdata.go
|
||||
@@ -7,6 +7,7 @@ package multipart
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
+ "fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/textproto"
|
||||
@@ -41,6 +42,9 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
|
||||
// Reserve an additional 10 MB for non-file parts.
|
||||
maxValueBytes := maxMemory + int64(10<<20)
|
||||
+ if maxValueBytes <= 0 {
|
||||
+ return nil, fmt.Errorf("multipart: integer overflow from maxMemory(%d) + 10MiB for non-file parts", maxMemory)
|
||||
+ }
|
||||
for {
|
||||
p, err := r.NextPart()
|
||||
if err == io.EOF {
|
||||
diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
|
||||
index 7d756c8c244a0..7112e0d3727fe 100644
|
||||
--- a/src/mime/multipart/formdata_test.go
|
||||
+++ b/src/mime/multipart/formdata_test.go
|
||||
@@ -7,6 +7,7 @@ package multipart
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
+ "math"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -52,6 +53,23 @@ func TestReadFormWithNamelessFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
+// Issue 40430: Ensure that we report integer overflows in additions of maxMemory,
|
||||
+// instead of silently and subtly failing without indication.
|
||||
+func TestReadFormMaxMemoryOverflow(t *testing.T) {
|
||||
+ b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n"))
|
||||
+ r := NewReader(b, boundary)
|
||||
+ f, err := r.ReadForm(math.MaxInt64)
|
||||
+ if err == nil {
|
||||
+ t.Fatal("Unexpected a non-nil error")
|
||||
+ }
|
||||
+ if f != nil {
|
||||
+ t.Fatalf("Unexpected returned a non-nil form: %v\n", f)
|
||||
+ }
|
||||
+ if g, w := err.Error(), "integer overflow from maxMemory"; !strings.Contains(g, w) {
|
||||
+ t.Errorf(`Error mismatch\n%q\ndid not contain\n%q`, g, w)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
func TestReadFormWithTextContentType(t *testing.T) {
|
||||
// From https://github.com/golang/go/issues/24041
|
||||
b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n"))
|
||||
97
meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre2.patch
Normal file
97
meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre2.patch
Normal file
@@ -0,0 +1,97 @@
|
||||
From 4e5a313524da62600eb59dbf98624cfe946456f8 Mon Sep 17 00:00:00 2001
|
||||
From: Emmanuel T Odeke <emmanuel@orijtech.com>
|
||||
Date: Tue, 20 Oct 2020 04:11:12 -0700
|
||||
Subject: [PATCH] net/http: test that ParseMultipartForm catches overflows
|
||||
|
||||
Tests that if the combination of:
|
||||
* HTTP multipart file payload size
|
||||
* ParseMultipartForm's maxMemory parameter
|
||||
* the internal leeway buffer size of 10MiB
|
||||
|
||||
overflows, then we'll report an overflow instead of silently
|
||||
passing.
|
||||
|
||||
Reapplies and fixes CL 254977, which was reverted in CL 263658.
|
||||
|
||||
The prior test lacked a res.Body.Close(), so fixed that and
|
||||
added a leaked Transport check to verify correctness.
|
||||
|
||||
Updates 40430.
|
||||
|
||||
Change-Id: I3c0f7ef43d621f6eb00f07755f04f9f36c51f98f
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/263817
|
||||
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
|
||||
TryBot-Result: Go Bot <gobot@golang.org>
|
||||
Reviewed-by: Bryan C. Mills <bcmills@google.com>
|
||||
Trust: Damien Neil <dneil@google.com>
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/4e5a313524da62600eb59dbf98624cfe946456f8]
|
||||
CVE: CVE-2022-41725 #Dependency Patch2
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
src/net/http/request_test.go | 45 ++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 45 insertions(+)
|
||||
|
||||
diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go
|
||||
index b4ef472e71229..19526b9ad791a 100644
|
||||
--- a/src/net/http/request_test.go
|
||||
+++ b/src/net/http/request_test.go
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
+ "math"
|
||||
"mime/multipart"
|
||||
. "net/http"
|
||||
"net/http/httptest"
|
||||
@@ -245,6 +246,50 @@ func TestParseMultipartForm(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
+// Issue #40430: Test that if maxMemory for ParseMultipartForm when combined with
|
||||
+// the payload size and the internal leeway buffer size of 10MiB overflows, that we
|
||||
+// correctly return an error.
|
||||
+func TestMaxInt64ForMultipartFormMaxMemoryOverflow(t *testing.T) {
|
||||
+ defer afterTest(t)
|
||||
+
|
||||
+ payloadSize := 1 << 10
|
||||
+ cst := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
|
||||
+ // The combination of:
|
||||
+ // MaxInt64 + payloadSize + (internal spare of 10MiB)
|
||||
+ // triggers the overflow. See issue https://golang.org/issue/40430/
|
||||
+ if err := req.ParseMultipartForm(math.MaxInt64); err != nil {
|
||||
+ Error(rw, err.Error(), StatusBadRequest)
|
||||
+ return
|
||||
+ }
|
||||
+ }))
|
||||
+ defer cst.Close()
|
||||
+ fBuf := new(bytes.Buffer)
|
||||
+ mw := multipart.NewWriter(fBuf)
|
||||
+ mf, err := mw.CreateFormFile("file", "myfile.txt")
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ if _, err := mf.Write(bytes.Repeat([]byte("abc"), payloadSize)); err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ if err := mw.Close(); err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ req, err := NewRequest("POST", cst.URL, fBuf)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ req.Header.Set("Content-Type", mw.FormDataContentType())
|
||||
+ res, err := cst.Client().Do(req)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ res.Body.Close()
|
||||
+ if g, w := res.StatusCode, StatusBadRequest; g != w {
|
||||
+ t.Fatalf("Status code mismatch: got %d, want %d", g, w)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
func TestRedirect_h1(t *testing.T) { testRedirect(t, h1Mode) }
|
||||
func TestRedirect_h2(t *testing.T) { testRedirect(t, h2Mode) }
|
||||
func testRedirect(t *testing.T, h2 bool) {
|
||||
98
meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre3.patch
Normal file
98
meta/recipes-devtools/go/go-1.14/CVE-2022-41725-pre3.patch
Normal file
@@ -0,0 +1,98 @@
|
||||
From 5246fa5e75b129a7dbd9722aa4de0cbaf7ceae43 Mon Sep 17 00:00:00 2001
|
||||
From: Russ Cox <rsc@golang.org>
|
||||
Date: Thu, 3 Dec 2020 09:45:07 -0500
|
||||
Subject: [PATCH] mime/multipart: handle ReadForm(math.MaxInt64) better
|
||||
|
||||
Returning an error about integer overflow is needlessly pedantic.
|
||||
The meaning of ReadForm(MaxInt64) is easily understood
|
||||
(accept a lot of data) and can be implemented.
|
||||
|
||||
Fixes #40430.
|
||||
|
||||
Change-Id: I8a522033dd9a2f9ad31dd2ad82cf08d553736ab9
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/275112
|
||||
Trust: Russ Cox <rsc@golang.org>
|
||||
Run-TryBot: Russ Cox <rsc@golang.org>
|
||||
TryBot-Result: Go Bot <gobot@golang.org>
|
||||
Reviewed-by: Ian Lance Taylor <iant@golang.org>
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/5246fa5e75b129a7dbd9722aa4de0cbaf7ceae43]
|
||||
CVE: CVE-2022-41725 #Dependency Patch3
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
src/mime/multipart/formdata.go | 8 ++++++--
|
||||
src/mime/multipart/formdata_test.go | 14 +++++---------
|
||||
src/net/http/request_test.go | 2 +-
|
||||
3 files changed, 12 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go
|
||||
index 4eb31012941ac..9c42ea8c023b5 100644
|
||||
--- a/src/mime/multipart/formdata.go
|
||||
+++ b/src/mime/multipart/formdata.go
|
||||
@@ -7,9 +7,9 @@ package multipart
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
- "fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
+ "math"
|
||||
"net/textproto"
|
||||
"os"
|
||||
)
|
||||
@@ -43,7 +43,11 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
// Reserve an additional 10 MB for non-file parts.
|
||||
maxValueBytes := maxMemory + int64(10<<20)
|
||||
if maxValueBytes <= 0 {
|
||||
- return nil, fmt.Errorf("multipart: integer overflow from maxMemory(%d) + 10MiB for non-file parts", maxMemory)
|
||||
+ if maxMemory < 0 {
|
||||
+ maxValueBytes = 0
|
||||
+ } else {
|
||||
+ maxValueBytes = math.MaxInt64
|
||||
+ }
|
||||
}
|
||||
for {
|
||||
p, err := r.NextPart()
|
||||
diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
|
||||
index 7112e0d3727fe..e3a3a3eae8e15 100644
|
||||
--- a/src/mime/multipart/formdata_test.go
|
||||
+++ b/src/mime/multipart/formdata_test.go
|
||||
@@ -53,20 +53,16 @@ func TestReadFormWithNamelessFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
-// Issue 40430: Ensure that we report integer overflows in additions of maxMemory,
|
||||
-// instead of silently and subtly failing without indication.
|
||||
+// Issue 40430: Handle ReadForm(math.MaxInt64)
|
||||
func TestReadFormMaxMemoryOverflow(t *testing.T) {
|
||||
b := strings.NewReader(strings.ReplaceAll(messageWithTextContentType, "\n", "\r\n"))
|
||||
r := NewReader(b, boundary)
|
||||
f, err := r.ReadForm(math.MaxInt64)
|
||||
- if err == nil {
|
||||
- t.Fatal("Unexpected a non-nil error")
|
||||
- }
|
||||
- if f != nil {
|
||||
- t.Fatalf("Unexpected returned a non-nil form: %v\n", f)
|
||||
+ if err != nil {
|
||||
+ t.Fatalf("ReadForm(MaxInt64): %v", err)
|
||||
}
|
||||
- if g, w := err.Error(), "integer overflow from maxMemory"; !strings.Contains(g, w) {
|
||||
- t.Errorf(`Error mismatch\n%q\ndid not contain\n%q`, g, w)
|
||||
+ if f == nil {
|
||||
+ t.Fatal("ReadForm(MaxInt64): missing form")
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go
|
||||
index 19526b9ad791a..689498e19d5dd 100644
|
||||
--- a/src/net/http/request_test.go
|
||||
+++ b/src/net/http/request_test.go
|
||||
@@ -285,7 +285,7 @@ func TestMaxInt64ForMultipartFormMaxMemoryOverflow(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
res.Body.Close()
|
||||
- if g, w := res.StatusCode, StatusBadRequest; g != w {
|
||||
+ if g, w := res.StatusCode, StatusOK; g != w {
|
||||
t.Fatalf("Status code mismatch: got %d, want %d", g, w)
|
||||
}
|
||||
}
|
||||
660
meta/recipes-devtools/go/go-1.14/CVE-2022-41725.patch
Normal file
660
meta/recipes-devtools/go/go-1.14/CVE-2022-41725.patch
Normal file
@@ -0,0 +1,660 @@
|
||||
From 5c55ac9bf1e5f779220294c843526536605f42ab Mon Sep 17 00:00:00 2001
|
||||
From: Damien Neil <dneil@google.com>
|
||||
Date: Wed, 25 Jan 2023 09:27:01 -0800
|
||||
Subject: [PATCH] [release-branch.go1.19] mime/multipart: limit memory/inode consumption of ReadForm
|
||||
|
||||
Reader.ReadForm is documented as storing "up to maxMemory bytes + 10MB"
|
||||
in memory. Parsed forms can consume substantially more memory than
|
||||
this limit, since ReadForm does not account for map entry overhead
|
||||
and MIME headers.
|
||||
|
||||
In addition, while the amount of disk memory consumed by ReadForm can
|
||||
be constrained by limiting the size of the parsed input, ReadForm will
|
||||
create one temporary file per form part stored on disk, potentially
|
||||
consuming a large number of inodes.
|
||||
|
||||
Update ReadForm's memory accounting to include part names,
|
||||
MIME headers, and map entry overhead.
|
||||
|
||||
Update ReadForm to store all on-disk file parts in a single
|
||||
temporary file.
|
||||
|
||||
Files returned by FileHeader.Open are documented as having a concrete
|
||||
type of *os.File when a file is stored on disk. The change to use a
|
||||
single temporary file for all parts means that this is no longer the
|
||||
case when a form contains more than a single file part stored on disk.
|
||||
|
||||
The previous behavior of storing each file part in a separate disk
|
||||
file may be reenabled with GODEBUG=multipartfiles=distinct.
|
||||
|
||||
Update Reader.NextPart and Reader.NextRawPart to set a 10MiB cap
|
||||
on the size of MIME headers.
|
||||
|
||||
Thanks to Jakob Ackermann (@das7pad) for reporting this issue.
|
||||
|
||||
Updates #58006
|
||||
Fixes #58362
|
||||
Fixes CVE-2022-41725
|
||||
|
||||
Change-Id: Ibd780a6c4c83ac8bcfd3cbe344f042e9940f2eab
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1714276
|
||||
Reviewed-by: Julie Qiu <julieqiu@google.com>
|
||||
TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com>
|
||||
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||||
Run-TryBot: Damien Neil <dneil@google.com>
|
||||
(cherry picked from commit ed4664330edcd91b24914c9371c377c132dbce8c)
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1728949
|
||||
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
|
||||
Run-TryBot: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/468116
|
||||
TryBot-Result: Gopher Robot <gobot@golang.org>
|
||||
Reviewed-by: Than McIntosh <thanm@google.com>
|
||||
Run-TryBot: Michael Pratt <mpratt@google.com>
|
||||
Auto-Submit: Michael Pratt <mpratt@google.com>
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/5c55ac9bf1e5f779220294c843526536605f42ab]
|
||||
CVE: CVE-2022-41725
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
src/mime/multipart/formdata.go | 132 ++++++++++++++++++++-----
|
||||
src/mime/multipart/formdata_test.go | 140 ++++++++++++++++++++++++++-
|
||||
src/mime/multipart/multipart.go | 25 +++--
|
||||
src/mime/multipart/readmimeheader.go | 14 +++
|
||||
src/net/http/request_test.go | 2 +-
|
||||
src/net/textproto/reader.go | 27 ++++++
|
||||
6 files changed, 303 insertions(+), 37 deletions(-)
|
||||
create mode 100644 src/mime/multipart/readmimeheader.go
|
||||
|
||||
diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go
|
||||
index 9c42ea8..1eeb340 100644
|
||||
--- a/src/mime/multipart/formdata.go
|
||||
+++ b/src/mime/multipart/formdata.go
|
||||
@@ -7,6 +7,7 @@ package multipart
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
+ "internal/godebug"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
@@ -34,23 +35,58 @@ func (r *Reader) ReadForm(maxMemory int64) (*Form, error) {
|
||||
|
||||
func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
form := &Form{make(map[string][]string), make(map[string][]*FileHeader)}
|
||||
+ var (
|
||||
+ file *os.File
|
||||
+ fileOff int64
|
||||
+ )
|
||||
+ numDiskFiles := 0
|
||||
+ multipartFiles := godebug.Get("multipartfiles")
|
||||
+ combineFiles := multipartFiles != "distinct"
|
||||
defer func() {
|
||||
+ if file != nil {
|
||||
+ if cerr := file.Close(); err == nil {
|
||||
+ err = cerr
|
||||
+ }
|
||||
+ }
|
||||
+ if combineFiles && numDiskFiles > 1 {
|
||||
+ for _, fhs := range form.File {
|
||||
+ for _, fh := range fhs {
|
||||
+ fh.tmpshared = true
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
if err != nil {
|
||||
form.RemoveAll()
|
||||
+ if file != nil {
|
||||
+ os.Remove(file.Name())
|
||||
+ }
|
||||
}
|
||||
}()
|
||||
|
||||
- // Reserve an additional 10 MB for non-file parts.
|
||||
- maxValueBytes := maxMemory + int64(10<<20)
|
||||
- if maxValueBytes <= 0 {
|
||||
+ // maxFileMemoryBytes is the maximum bytes of file data we will store in memory.
|
||||
+ // Data past this limit is written to disk.
|
||||
+ // This limit strictly applies to content, not metadata (filenames, MIME headers, etc.),
|
||||
+ // since metadata is always stored in memory, not disk.
|
||||
+ //
|
||||
+ // maxMemoryBytes is the maximum bytes we will store in memory, including file content,
|
||||
+ // non-file part values, metdata, and map entry overhead.
|
||||
+ //
|
||||
+ // We reserve an additional 10 MB in maxMemoryBytes for non-file data.
|
||||
+ //
|
||||
+ // The relationship between these parameters, as well as the overly-large and
|
||||
+ // unconfigurable 10 MB added on to maxMemory, is unfortunate but difficult to change
|
||||
+ // within the constraints of the API as documented.
|
||||
+ maxFileMemoryBytes := maxMemory
|
||||
+ maxMemoryBytes := maxMemory + int64(10<<20)
|
||||
+ if maxMemoryBytes <= 0 {
|
||||
if maxMemory < 0 {
|
||||
- maxValueBytes = 0
|
||||
+ maxMemoryBytes = 0
|
||||
} else {
|
||||
- maxValueBytes = math.MaxInt64
|
||||
+ maxMemoryBytes = math.MaxInt64
|
||||
}
|
||||
}
|
||||
for {
|
||||
- p, err := r.NextPart()
|
||||
+ p, err := r.nextPart(false, maxMemoryBytes)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
@@ -64,16 +100,27 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
}
|
||||
filename := p.FileName()
|
||||
|
||||
+ // Multiple values for the same key (one map entry, longer slice) are cheaper
|
||||
+ // than the same number of values for different keys (many map entries), but
|
||||
+ // using a consistent per-value cost for overhead is simpler.
|
||||
+ maxMemoryBytes -= int64(len(name))
|
||||
+ maxMemoryBytes -= 100 // map overhead
|
||||
+ if maxMemoryBytes < 0 {
|
||||
+ // We can't actually take this path, since nextPart would already have
|
||||
+ // rejected the MIME headers for being too large. Check anyway.
|
||||
+ return nil, ErrMessageTooLarge
|
||||
+ }
|
||||
+
|
||||
var b bytes.Buffer
|
||||
|
||||
if filename == "" {
|
||||
// value, store as string in memory
|
||||
- n, err := io.CopyN(&b, p, maxValueBytes+1)
|
||||
+ n, err := io.CopyN(&b, p, maxMemoryBytes+1)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
- maxValueBytes -= n
|
||||
- if maxValueBytes < 0 {
|
||||
+ maxMemoryBytes -= n
|
||||
+ if maxMemoryBytes < 0 {
|
||||
return nil, ErrMessageTooLarge
|
||||
}
|
||||
form.Value[name] = append(form.Value[name], b.String())
|
||||
@@ -81,35 +128,45 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
}
|
||||
|
||||
// file, store in memory or on disk
|
||||
+ maxMemoryBytes -= mimeHeaderSize(p.Header)
|
||||
+ if maxMemoryBytes < 0 {
|
||||
+ return nil, ErrMessageTooLarge
|
||||
+ }
|
||||
fh := &FileHeader{
|
||||
Filename: filename,
|
||||
Header: p.Header,
|
||||
}
|
||||
- n, err := io.CopyN(&b, p, maxMemory+1)
|
||||
+ n, err := io.CopyN(&b, p, maxFileMemoryBytes+1)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, err
|
||||
}
|
||||
- if n > maxMemory {
|
||||
- // too big, write to disk and flush buffer
|
||||
- file, err := ioutil.TempFile("", "multipart-")
|
||||
- if err != nil {
|
||||
- return nil, err
|
||||
+ if n > maxFileMemoryBytes {
|
||||
+ if file == nil {
|
||||
+ file, err = ioutil.TempFile(r.tempDir, "multipart-")
|
||||
+ if err != nil {
|
||||
+ return nil, err
|
||||
+ }
|
||||
}
|
||||
+ numDiskFiles++
|
||||
size, err := io.Copy(file, io.MultiReader(&b, p))
|
||||
- if cerr := file.Close(); err == nil {
|
||||
- err = cerr
|
||||
- }
|
||||
if err != nil {
|
||||
- os.Remove(file.Name())
|
||||
return nil, err
|
||||
}
|
||||
fh.tmpfile = file.Name()
|
||||
fh.Size = size
|
||||
+ fh.tmpoff = fileOff
|
||||
+ fileOff += size
|
||||
+ if !combineFiles {
|
||||
+ if err := file.Close(); err != nil {
|
||||
+ return nil, err
|
||||
+ }
|
||||
+ file = nil
|
||||
+ }
|
||||
} else {
|
||||
fh.content = b.Bytes()
|
||||
fh.Size = int64(len(fh.content))
|
||||
- maxMemory -= n
|
||||
- maxValueBytes -= n
|
||||
+ maxFileMemoryBytes -= n
|
||||
+ maxMemoryBytes -= n
|
||||
}
|
||||
form.File[name] = append(form.File[name], fh)
|
||||
}
|
||||
@@ -117,6 +174,17 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
return form, nil
|
||||
}
|
||||
|
||||
+func mimeHeaderSize(h textproto.MIMEHeader) (size int64) {
|
||||
+ for k, vs := range h {
|
||||
+ size += int64(len(k))
|
||||
+ size += 100 // map entry overhead
|
||||
+ for _, v := range vs {
|
||||
+ size += int64(len(v))
|
||||
+ }
|
||||
+ }
|
||||
+ return size
|
||||
+}
|
||||
+
|
||||
// Form is a parsed multipart form.
|
||||
// Its File parts are stored either in memory or on disk,
|
||||
// and are accessible via the *FileHeader's Open method.
|
||||
@@ -134,7 +202,7 @@ func (f *Form) RemoveAll() error {
|
||||
for _, fh := range fhs {
|
||||
if fh.tmpfile != "" {
|
||||
e := os.Remove(fh.tmpfile)
|
||||
- if e != nil && err == nil {
|
||||
+ if e != nil && !errors.Is(e, os.ErrNotExist) && err == nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
@@ -149,15 +217,25 @@ type FileHeader struct {
|
||||
Header textproto.MIMEHeader
|
||||
Size int64
|
||||
|
||||
- content []byte
|
||||
- tmpfile string
|
||||
+ content []byte
|
||||
+ tmpfile string
|
||||
+ tmpoff int64
|
||||
+ tmpshared bool
|
||||
}
|
||||
|
||||
// Open opens and returns the FileHeader's associated File.
|
||||
func (fh *FileHeader) Open() (File, error) {
|
||||
if b := fh.content; b != nil {
|
||||
r := io.NewSectionReader(bytes.NewReader(b), 0, int64(len(b)))
|
||||
- return sectionReadCloser{r}, nil
|
||||
+ return sectionReadCloser{r, nil}, nil
|
||||
+ }
|
||||
+ if fh.tmpshared {
|
||||
+ f, err := os.Open(fh.tmpfile)
|
||||
+ if err != nil {
|
||||
+ return nil, err
|
||||
+ }
|
||||
+ r := io.NewSectionReader(f, fh.tmpoff, fh.Size)
|
||||
+ return sectionReadCloser{r, f}, nil
|
||||
}
|
||||
return os.Open(fh.tmpfile)
|
||||
}
|
||||
@@ -176,8 +254,12 @@ type File interface {
|
||||
|
||||
type sectionReadCloser struct {
|
||||
*io.SectionReader
|
||||
+ io.Closer
|
||||
}
|
||||
|
||||
func (rc sectionReadCloser) Close() error {
|
||||
+ if rc.Closer != nil {
|
||||
+ return rc.Closer.Close()
|
||||
+ }
|
||||
return nil
|
||||
}
|
||||
diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
|
||||
index e3a3a3e..5cded71 100644
|
||||
--- a/src/mime/multipart/formdata_test.go
|
||||
+++ b/src/mime/multipart/formdata_test.go
|
||||
@@ -6,8 +6,10 @@ package multipart
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
+ "fmt"
|
||||
"io"
|
||||
"math"
|
||||
+ "net/textproto"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -208,8 +210,8 @@ Content-Disposition: form-data; name="largetext"
|
||||
maxMemory int64
|
||||
err error
|
||||
}{
|
||||
- {"smaller", 50, nil},
|
||||
- {"exact-fit", 25, nil},
|
||||
+ {"smaller", 50 + int64(len("largetext")) + 100, nil},
|
||||
+ {"exact-fit", 25 + int64(len("largetext")) + 100, nil},
|
||||
{"too-large", 0, ErrMessageTooLarge},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
@@ -224,7 +226,7 @@ Content-Disposition: form-data; name="largetext"
|
||||
defer f.RemoveAll()
|
||||
}
|
||||
if tc.err != err {
|
||||
- t.Fatalf("ReadForm error - got: %v; expected: %v", tc.err, err)
|
||||
+ t.Fatalf("ReadForm error - got: %v; expected: %v", err, tc.err)
|
||||
}
|
||||
if err == nil {
|
||||
if g := f.Value["largetext"][0]; g != largeTextValue {
|
||||
@@ -234,3 +236,135 @@ Content-Disposition: form-data; name="largetext"
|
||||
})
|
||||
}
|
||||
}
|
||||
+
|
||||
+// TestReadForm_MetadataTooLarge verifies that we account for the size of field names,
|
||||
+// MIME headers, and map entry overhead while limiting the memory consumption of parsed forms.
|
||||
+func TestReadForm_MetadataTooLarge(t *testing.T) {
|
||||
+ for _, test := range []struct {
|
||||
+ name string
|
||||
+ f func(*Writer)
|
||||
+ }{{
|
||||
+ name: "large name",
|
||||
+ f: func(fw *Writer) {
|
||||
+ name := strings.Repeat("a", 10<<20)
|
||||
+ w, _ := fw.CreateFormField(name)
|
||||
+ w.Write([]byte("value"))
|
||||
+ },
|
||||
+ }, {
|
||||
+ name: "large MIME header",
|
||||
+ f: func(fw *Writer) {
|
||||
+ h := make(textproto.MIMEHeader)
|
||||
+ h.Set("Content-Disposition", `form-data; name="a"`)
|
||||
+ h.Set("X-Foo", strings.Repeat("a", 10<<20))
|
||||
+ w, _ := fw.CreatePart(h)
|
||||
+ w.Write([]byte("value"))
|
||||
+ },
|
||||
+ }, {
|
||||
+ name: "many parts",
|
||||
+ f: func(fw *Writer) {
|
||||
+ for i := 0; i < 110000; i++ {
|
||||
+ w, _ := fw.CreateFormField("f")
|
||||
+ w.Write([]byte("v"))
|
||||
+ }
|
||||
+ },
|
||||
+ }} {
|
||||
+ t.Run(test.name, func(t *testing.T) {
|
||||
+ var buf bytes.Buffer
|
||||
+ fw := NewWriter(&buf)
|
||||
+ test.f(fw)
|
||||
+ if err := fw.Close(); err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ fr := NewReader(&buf, fw.Boundary())
|
||||
+ _, err := fr.ReadForm(0)
|
||||
+ if err != ErrMessageTooLarge {
|
||||
+ t.Errorf("fr.ReadForm() = %v, want ErrMessageTooLarge", err)
|
||||
+ }
|
||||
+ })
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// TestReadForm_ManyFiles_Combined tests that a multipart form containing many files only
|
||||
+// results in a single on-disk file.
|
||||
+func TestReadForm_ManyFiles_Combined(t *testing.T) {
|
||||
+ const distinct = false
|
||||
+ testReadFormManyFiles(t, distinct)
|
||||
+}
|
||||
+
|
||||
+// TestReadForm_ManyFiles_Distinct tests that setting GODEBUG=multipartfiles=distinct
|
||||
+// results in every file in a multipart form being placed in a distinct on-disk file.
|
||||
+func TestReadForm_ManyFiles_Distinct(t *testing.T) {
|
||||
+ t.Setenv("GODEBUG", "multipartfiles=distinct")
|
||||
+ const distinct = true
|
||||
+ testReadFormManyFiles(t, distinct)
|
||||
+}
|
||||
+
|
||||
+func testReadFormManyFiles(t *testing.T, distinct bool) {
|
||||
+ var buf bytes.Buffer
|
||||
+ fw := NewWriter(&buf)
|
||||
+ const numFiles = 10
|
||||
+ for i := 0; i < numFiles; i++ {
|
||||
+ name := fmt.Sprint(i)
|
||||
+ w, err := fw.CreateFormFile(name, name)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ w.Write([]byte(name))
|
||||
+ }
|
||||
+ if err := fw.Close(); err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ fr := NewReader(&buf, fw.Boundary())
|
||||
+ fr.tempDir = t.TempDir()
|
||||
+ form, err := fr.ReadForm(0)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ for i := 0; i < numFiles; i++ {
|
||||
+ name := fmt.Sprint(i)
|
||||
+ if got := len(form.File[name]); got != 1 {
|
||||
+ t.Fatalf("form.File[%q] has %v entries, want 1", name, got)
|
||||
+ }
|
||||
+ fh := form.File[name][0]
|
||||
+ file, err := fh.Open()
|
||||
+ if err != nil {
|
||||
+ t.Fatalf("form.File[%q].Open() = %v", name, err)
|
||||
+ }
|
||||
+ if distinct {
|
||||
+ if _, ok := file.(*os.File); !ok {
|
||||
+ t.Fatalf("form.File[%q].Open: %T, want *os.File", name, file)
|
||||
+ }
|
||||
+ }
|
||||
+ got, err := io.ReadAll(file)
|
||||
+ file.Close()
|
||||
+ if string(got) != name || err != nil {
|
||||
+ t.Fatalf("read form.File[%q]: %q, %v; want %q, nil", name, string(got), err, name)
|
||||
+ }
|
||||
+ }
|
||||
+ dir, err := os.Open(fr.tempDir)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ defer dir.Close()
|
||||
+ names, err := dir.Readdirnames(0)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ wantNames := 1
|
||||
+ if distinct {
|
||||
+ wantNames = numFiles
|
||||
+ }
|
||||
+ if len(names) != wantNames {
|
||||
+ t.Fatalf("temp dir contains %v files; want 1", len(names))
|
||||
+ }
|
||||
+ if err := form.RemoveAll(); err != nil {
|
||||
+ t.Fatalf("form.RemoveAll() = %v", err)
|
||||
+ }
|
||||
+ names, err = dir.Readdirnames(0)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ if len(names) != 0 {
|
||||
+ t.Fatalf("temp dir contains %v files; want 0", len(names))
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go
|
||||
index 1750300..958cef8 100644
|
||||
--- a/src/mime/multipart/multipart.go
|
||||
+++ b/src/mime/multipart/multipart.go
|
||||
@@ -121,12 +121,12 @@ func (r *stickyErrorReader) Read(p []byte) (n int, _ error) {
|
||||
return n, r.err
|
||||
}
|
||||
|
||||
-func newPart(mr *Reader, rawPart bool) (*Part, error) {
|
||||
+func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize int64) (*Part, error) {
|
||||
bp := &Part{
|
||||
Header: make(map[string][]string),
|
||||
mr: mr,
|
||||
}
|
||||
- if err := bp.populateHeaders(); err != nil {
|
||||
+ if err := bp.populateHeaders(maxMIMEHeaderSize); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bp.r = partReader{bp}
|
||||
@@ -142,12 +142,16 @@ func newPart(mr *Reader, rawPart bool) (*Part, error) {
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
-func (bp *Part) populateHeaders() error {
|
||||
+func (bp *Part) populateHeaders(maxMIMEHeaderSize int64) error {
|
||||
r := textproto.NewReader(bp.mr.bufReader)
|
||||
- header, err := r.ReadMIMEHeader()
|
||||
+ header, err := readMIMEHeader(r, maxMIMEHeaderSize)
|
||||
if err == nil {
|
||||
bp.Header = header
|
||||
}
|
||||
+ // TODO: Add a distinguishable error to net/textproto.
|
||||
+ if err != nil && err.Error() == "message too large" {
|
||||
+ err = ErrMessageTooLarge
|
||||
+ }
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -287,6 +291,7 @@ func (p *Part) Close() error {
|
||||
// isn't supported.
|
||||
type Reader struct {
|
||||
bufReader *bufio.Reader
|
||||
+ tempDir string // used in tests
|
||||
|
||||
currentPart *Part
|
||||
partsRead int
|
||||
@@ -297,6 +302,10 @@ type Reader struct {
|
||||
dashBoundary []byte // "--boundary"
|
||||
}
|
||||
|
||||
+// maxMIMEHeaderSize is the maximum size of a MIME header we will parse,
|
||||
+// including header keys, values, and map overhead.
|
||||
+const maxMIMEHeaderSize = 10 << 20
|
||||
+
|
||||
// NextPart returns the next part in the multipart or an error.
|
||||
// When there are no more parts, the error io.EOF is returned.
|
||||
//
|
||||
@@ -304,7 +313,7 @@ type Reader struct {
|
||||
// has a value of "quoted-printable", that header is instead
|
||||
// hidden and the body is transparently decoded during Read calls.
|
||||
func (r *Reader) NextPart() (*Part, error) {
|
||||
- return r.nextPart(false)
|
||||
+ return r.nextPart(false, maxMIMEHeaderSize)
|
||||
}
|
||||
|
||||
// NextRawPart returns the next part in the multipart or an error.
|
||||
@@ -313,10 +322,10 @@ func (r *Reader) NextPart() (*Part, error) {
|
||||
// Unlike NextPart, it does not have special handling for
|
||||
// "Content-Transfer-Encoding: quoted-printable".
|
||||
func (r *Reader) NextRawPart() (*Part, error) {
|
||||
- return r.nextPart(true)
|
||||
+ return r.nextPart(true, maxMIMEHeaderSize)
|
||||
}
|
||||
|
||||
-func (r *Reader) nextPart(rawPart bool) (*Part, error) {
|
||||
+func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize int64) (*Part, error) {
|
||||
if r.currentPart != nil {
|
||||
r.currentPart.Close()
|
||||
}
|
||||
@@ -341,7 +350,7 @@ func (r *Reader) nextPart(rawPart bool) (*Part, error) {
|
||||
|
||||
if r.isBoundaryDelimiterLine(line) {
|
||||
r.partsRead++
|
||||
- bp, err := newPart(r, rawPart)
|
||||
+ bp, err := newPart(r, rawPart, maxMIMEHeaderSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diff --git a/src/mime/multipart/readmimeheader.go b/src/mime/multipart/readmimeheader.go
|
||||
new file mode 100644
|
||||
index 0000000..6836928
|
||||
--- /dev/null
|
||||
+++ b/src/mime/multipart/readmimeheader.go
|
||||
@@ -0,0 +1,14 @@
|
||||
+// Copyright 2023 The Go Authors. All rights reserved.
|
||||
+// Use of this source code is governed by a BSD-style
|
||||
+// license that can be found in the LICENSE file.
|
||||
+package multipart
|
||||
+
|
||||
+import (
|
||||
+ "net/textproto"
|
||||
+ _ "unsafe" // for go:linkname
|
||||
+)
|
||||
+
|
||||
+// readMIMEHeader is defined in package net/textproto.
|
||||
+//
|
||||
+//go:linkname readMIMEHeader net/textproto.readMIMEHeader
|
||||
+func readMIMEHeader(r *textproto.Reader, lim int64) (textproto.MIMEHeader, error)
|
||||
diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go
|
||||
index 94133ee..170d3f5 100644
|
||||
--- a/src/net/http/request_test.go
|
||||
+++ b/src/net/http/request_test.go
|
||||
@@ -962,7 +962,7 @@ func testMissingFile(t *testing.T, req *Request) {
|
||||
t.Errorf("FormFile file = %v, want nil", f)
|
||||
}
|
||||
if fh != nil {
|
||||
- t.Errorf("FormFile file header = %q, want nil", fh)
|
||||
+ t.Errorf("FormFile file header = %v, want nil", fh)
|
||||
}
|
||||
if err != ErrMissingFile {
|
||||
t.Errorf("FormFile err = %q, want ErrMissingFile", err)
|
||||
diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go
|
||||
index f63f5ec..96553fb 100644
|
||||
--- a/src/net/textproto/reader.go
|
||||
+++ b/src/net/textproto/reader.go
|
||||
@@ -7,9 +7,11 @@ package textproto
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
+ "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
+ "math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -482,6 +484,12 @@ func (r *Reader) ReadDotLines() ([]string, error) {
|
||||
// }
|
||||
//
|
||||
func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
|
||||
+ return readMIMEHeader(r, math.MaxInt64)
|
||||
+}
|
||||
+
|
||||
+// readMIMEHeader is a version of ReadMIMEHeader which takes a limit on the header size.
|
||||
+// It is called by the mime/multipart package.
|
||||
+func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
|
||||
// Avoid lots of small slice allocations later by allocating one
|
||||
// large one ahead of time which we'll cut up into smaller
|
||||
// slices. If this isn't big enough later, we allocate small ones.
|
||||
@@ -525,6 +533,15 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
+ // backport 5c55ac9bf1e5f779220294c843526536605f42ab
|
||||
+ //
|
||||
+ // value is computed as
|
||||
+ // value := string(bytes.TrimLeft(v, " \t"))
|
||||
+ //
|
||||
+ // in the original patch from 1.19. This relies on
|
||||
+ // 'v' which does not exist in 1.14. We leave the
|
||||
+ // 1.14 method unchanged.
|
||||
+
|
||||
// Skip initial spaces in value.
|
||||
i++ // skip colon
|
||||
for i < len(kv) && (kv[i] == ' ' || kv[i] == '\t') {
|
||||
@@ -533,6 +550,16 @@ func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
|
||||
value := string(kv[i:])
|
||||
|
||||
vv := m[key]
|
||||
+ if vv == nil {
|
||||
+ lim -= int64(len(key))
|
||||
+ lim -= 100 // map entry overhead
|
||||
+ }
|
||||
+ lim -= int64(len(value))
|
||||
+ if lim < 0 {
|
||||
+ // TODO: This should be a distinguishable error (ErrMessageTooLarge)
|
||||
+ // to allow mime/multipart to detect it.
|
||||
+ return m, errors.New("message too large")
|
||||
+ }
|
||||
if vv == nil && len(strs) > 0 {
|
||||
// More than likely this will be a single-element key.
|
||||
// Most headers aren't multi-valued.
|
||||
--
|
||||
2.25.1
|
||||
|
||||
134
meta/recipes-devtools/go/go-1.14/CVE-2023-24536_1.patch
Normal file
134
meta/recipes-devtools/go/go-1.14/CVE-2023-24536_1.patch
Normal file
@@ -0,0 +1,134 @@
|
||||
From ef41a4e2face45e580c5836eaebd51629fc23f15 Mon Sep 17 00:00:00 2001
|
||||
From: Damien Neil <dneil@google.com>
|
||||
Date: Thu, 16 Mar 2023 14:18:04 -0700
|
||||
Subject: [PATCH] [release-branch.go1.19] mime/multipart: avoid excessive copy
|
||||
buffer allocations in ReadForm
|
||||
|
||||
When copying form data to disk with io.Copy,
|
||||
allocate only one copy buffer and reuse it rather than
|
||||
creating two buffers per file (one from io.multiReader.WriteTo,
|
||||
and a second one from os.File.ReadFrom).
|
||||
|
||||
Thanks to Jakob Ackermann (@das7pad) for reporting this issue.
|
||||
|
||||
For CVE-2023-24536
|
||||
For #59153
|
||||
For #59269
|
||||
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802453
|
||||
Run-TryBot: Damien Neil <dneil@google.com>
|
||||
Reviewed-by: Julie Qiu <julieqiu@google.com>
|
||||
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802395
|
||||
Run-TryBot: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Change-Id: Ie405470c92abffed3356913b37d813e982c96c8b
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/481983
|
||||
Run-TryBot: Michael Knyszek <mknyszek@google.com>
|
||||
TryBot-Result: Gopher Robot <gobot@golang.org>
|
||||
Auto-Submit: Michael Knyszek <mknyszek@google.com>
|
||||
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/ef41a4e2face45e580c5836eaebd51629fc23f15]
|
||||
CVE: CVE-2023-24536
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
src/mime/multipart/formdata.go | 15 +++++++--
|
||||
src/mime/multipart/formdata_test.go | 49 +++++++++++++++++++++++++++++
|
||||
2 files changed, 61 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go
|
||||
index a7d4ca97f0484..975dcb6b26db4 100644
|
||||
--- a/src/mime/multipart/formdata.go
|
||||
+++ b/src/mime/multipart/formdata.go
|
||||
@@ -84,6 +84,7 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
maxMemoryBytes = math.MaxInt64
|
||||
}
|
||||
}
|
||||
+ var copyBuf []byte
|
||||
for {
|
||||
p, err := r.nextPart(false, maxMemoryBytes)
|
||||
if err == io.EOF {
|
||||
@@ -147,14 +148,22 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
}
|
||||
}
|
||||
numDiskFiles++
|
||||
- size, err := io.Copy(file, io.MultiReader(&b, p))
|
||||
+ if _, err := file.Write(b.Bytes()); err != nil {
|
||||
+ return nil, err
|
||||
+ }
|
||||
+ if copyBuf == nil {
|
||||
+ copyBuf = make([]byte, 32*1024) // same buffer size as io.Copy uses
|
||||
+ }
|
||||
+ // os.File.ReadFrom will allocate its own copy buffer if we let io.Copy use it.
|
||||
+ type writerOnly struct{ io.Writer }
|
||||
+ remainingSize, err := io.CopyBuffer(writerOnly{file}, p, copyBuf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fh.tmpfile = file.Name()
|
||||
- fh.Size = size
|
||||
+ fh.Size = int64(b.Len()) + remainingSize
|
||||
fh.tmpoff = fileOff
|
||||
- fileOff += size
|
||||
+ fileOff += fh.Size
|
||||
if !combineFiles {
|
||||
if err := file.Close(); err != nil {
|
||||
return nil, err
|
||||
diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
|
||||
index 5cded7170c6b8..f5b56083b2377 100644
|
||||
--- a/src/mime/multipart/formdata_test.go
|
||||
+++ b/src/mime/multipart/formdata_test.go
|
||||
@@ -368,3 +368,52 @@ func testReadFormManyFiles(t *testing.T, distinct bool) {
|
||||
t.Fatalf("temp dir contains %v files; want 0", len(names))
|
||||
}
|
||||
}
|
||||
+
|
||||
+func BenchmarkReadForm(b *testing.B) {
|
||||
+ for _, test := range []struct {
|
||||
+ name string
|
||||
+ form func(fw *Writer, count int)
|
||||
+ }{{
|
||||
+ name: "fields",
|
||||
+ form: func(fw *Writer, count int) {
|
||||
+ for i := 0; i < count; i++ {
|
||||
+ w, _ := fw.CreateFormField(fmt.Sprintf("field%v", i))
|
||||
+ fmt.Fprintf(w, "value %v", i)
|
||||
+ }
|
||||
+ },
|
||||
+ }, {
|
||||
+ name: "files",
|
||||
+ form: func(fw *Writer, count int) {
|
||||
+ for i := 0; i < count; i++ {
|
||||
+ w, _ := fw.CreateFormFile(fmt.Sprintf("field%v", i), fmt.Sprintf("file%v", i))
|
||||
+ fmt.Fprintf(w, "value %v", i)
|
||||
+ }
|
||||
+ },
|
||||
+ }} {
|
||||
+ b.Run(test.name, func(b *testing.B) {
|
||||
+ for _, maxMemory := range []int64{
|
||||
+ 0,
|
||||
+ 1 << 20,
|
||||
+ } {
|
||||
+ var buf bytes.Buffer
|
||||
+ fw := NewWriter(&buf)
|
||||
+ test.form(fw, 10)
|
||||
+ if err := fw.Close(); err != nil {
|
||||
+ b.Fatal(err)
|
||||
+ }
|
||||
+ b.Run(fmt.Sprintf("maxMemory=%v", maxMemory), func(b *testing.B) {
|
||||
+ b.ReportAllocs()
|
||||
+ for i := 0; i < b.N; i++ {
|
||||
+ fr := NewReader(bytes.NewReader(buf.Bytes()), fw.Boundary())
|
||||
+ form, err := fr.ReadForm(maxMemory)
|
||||
+ if err != nil {
|
||||
+ b.Fatal(err)
|
||||
+ }
|
||||
+ form.RemoveAll()
|
||||
+ }
|
||||
+
|
||||
+ })
|
||||
+ }
|
||||
+ })
|
||||
+ }
|
||||
+}
|
||||
184
meta/recipes-devtools/go/go-1.14/CVE-2023-24536_2.patch
Normal file
184
meta/recipes-devtools/go/go-1.14/CVE-2023-24536_2.patch
Normal file
@@ -0,0 +1,184 @@
|
||||
From 7a359a651c7ebdb29e0a1c03102fce793e9f58f0 Mon Sep 17 00:00:00 2001
|
||||
From: Damien Neil <dneil@google.com>
|
||||
Date: Thu, 16 Mar 2023 16:56:12 -0700
|
||||
Subject: [PATCH] [release-branch.go1.19] net/textproto, mime/multipart:
|
||||
improve accounting of non-file data
|
||||
|
||||
For requests containing large numbers of small parts,
|
||||
memory consumption of a parsed form could be about 250%
|
||||
over the estimated size.
|
||||
|
||||
When considering the size of parsed forms, account for the size of
|
||||
FileHeader structs and increase the estimate of memory consumed by
|
||||
map entries.
|
||||
|
||||
Thanks to Jakob Ackermann (@das7pad) for reporting this issue.
|
||||
|
||||
For CVE-2023-24536
|
||||
For #59153
|
||||
For #59269
|
||||
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802454
|
||||
Run-TryBot: Damien Neil <dneil@google.com>
|
||||
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-by: Julie Qiu <julieqiu@google.com>
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802396
|
||||
Run-TryBot: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Change-Id: I31bc50e9346b4eee6fbe51a18c3c57230cc066db
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/481984
|
||||
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
|
||||
Auto-Submit: Michael Knyszek <mknyszek@google.com>
|
||||
TryBot-Result: Gopher Robot <gobot@golang.org>
|
||||
Run-TryBot: Michael Knyszek <mknyszek@google.com>
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/7a359a651c7ebdb29e0a1c03102fce793e9f58f0]
|
||||
CVE: CVE-2023-24536
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
src/mime/multipart/formdata.go | 9 +++--
|
||||
src/mime/multipart/formdata_test.go | 55 ++++++++++++-----------------
|
||||
src/net/textproto/reader.go | 8 ++++-
|
||||
3 files changed, 37 insertions(+), 35 deletions(-)
|
||||
|
||||
diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go
|
||||
index 975dcb6b26db4..3f6ff697ca608 100644
|
||||
--- a/src/mime/multipart/formdata.go
|
||||
+++ b/src/mime/multipart/formdata.go
|
||||
@@ -103,8 +103,9 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
// Multiple values for the same key (one map entry, longer slice) are cheaper
|
||||
// than the same number of values for different keys (many map entries), but
|
||||
// using a consistent per-value cost for overhead is simpler.
|
||||
+ const mapEntryOverhead = 200
|
||||
maxMemoryBytes -= int64(len(name))
|
||||
- maxMemoryBytes -= 100 // map overhead
|
||||
+ maxMemoryBytes -= mapEntryOverhead
|
||||
if maxMemoryBytes < 0 {
|
||||
// We can't actually take this path, since nextPart would already have
|
||||
// rejected the MIME headers for being too large. Check anyway.
|
||||
@@ -128,7 +129,10 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
}
|
||||
|
||||
// file, store in memory or on disk
|
||||
+ const fileHeaderSize = 100
|
||||
maxMemoryBytes -= mimeHeaderSize(p.Header)
|
||||
+ maxMemoryBytes -= mapEntryOverhead
|
||||
+ maxMemoryBytes -= fileHeaderSize
|
||||
if maxMemoryBytes < 0 {
|
||||
return nil, ErrMessageTooLarge
|
||||
}
|
||||
@@ -183,9 +187,10 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
}
|
||||
|
||||
func mimeHeaderSize(h textproto.MIMEHeader) (size int64) {
|
||||
+ size = 400
|
||||
for k, vs := range h {
|
||||
size += int64(len(k))
|
||||
- size += 100 // map entry overhead
|
||||
+ size += 200 // map entry overhead
|
||||
for _, v := range vs {
|
||||
size += int64(len(v))
|
||||
}
|
||||
diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
|
||||
index f5b56083b2377..8ed26e0c34081 100644
|
||||
--- a/src/mime/multipart/formdata_test.go
|
||||
+++ b/src/mime/multipart/formdata_test.go
|
||||
@@ -192,10 +192,10 @@ func (r *failOnReadAfterErrorReader) Read(p []byte) (n int, err error) {
|
||||
// TestReadForm_NonFileMaxMemory asserts that the ReadForm maxMemory limit is applied
|
||||
// while processing non-file form data as well as file form data.
|
||||
func TestReadForm_NonFileMaxMemory(t *testing.T) {
|
||||
- n := 10<<20 + 25
|
||||
if testing.Short() {
|
||||
- n = 10<<10 + 25
|
||||
+ t.Skip("skipping in -short mode")
|
||||
}
|
||||
+ n := 10 << 20
|
||||
largeTextValue := strings.Repeat("1", n)
|
||||
message := `--MyBoundary
|
||||
Content-Disposition: form-data; name="largetext"
|
||||
@@ -203,38 +203,29 @@ Content-Disposition: form-data; name="largetext"
|
||||
` + largeTextValue + `
|
||||
--MyBoundary--
|
||||
`
|
||||
-
|
||||
testBody := strings.ReplaceAll(message, "\n", "\r\n")
|
||||
- testCases := []struct {
|
||||
- name string
|
||||
- maxMemory int64
|
||||
- err error
|
||||
- }{
|
||||
- {"smaller", 50 + int64(len("largetext")) + 100, nil},
|
||||
- {"exact-fit", 25 + int64(len("largetext")) + 100, nil},
|
||||
- {"too-large", 0, ErrMessageTooLarge},
|
||||
- }
|
||||
- for _, tc := range testCases {
|
||||
- t.Run(tc.name, func(t *testing.T) {
|
||||
- if tc.maxMemory == 0 && testing.Short() {
|
||||
- t.Skip("skipping in -short mode")
|
||||
- }
|
||||
- b := strings.NewReader(testBody)
|
||||
- r := NewReader(b, boundary)
|
||||
- f, err := r.ReadForm(tc.maxMemory)
|
||||
- if err == nil {
|
||||
- defer f.RemoveAll()
|
||||
- }
|
||||
- if tc.err != err {
|
||||
- t.Fatalf("ReadForm error - got: %v; expected: %v", err, tc.err)
|
||||
- }
|
||||
- if err == nil {
|
||||
- if g := f.Value["largetext"][0]; g != largeTextValue {
|
||||
- t.Errorf("largetext mismatch: got size: %v, expected size: %v", len(g), len(largeTextValue))
|
||||
- }
|
||||
- }
|
||||
- })
|
||||
+ // Try parsing the form with increasing maxMemory values.
|
||||
+ // Changes in how we account for non-file form data may cause the exact point
|
||||
+ // where we change from rejecting the form as too large to accepting it to vary,
|
||||
+ // but we should see both successes and failures.
|
||||
+ const failWhenMaxMemoryLessThan = 128
|
||||
+ for maxMemory := int64(0); maxMemory < failWhenMaxMemoryLessThan*2; maxMemory += 16 {
|
||||
+ b := strings.NewReader(testBody)
|
||||
+ r := NewReader(b, boundary)
|
||||
+ f, err := r.ReadForm(maxMemory)
|
||||
+ if err != nil {
|
||||
+ continue
|
||||
+ }
|
||||
+ if g := f.Value["largetext"][0]; g != largeTextValue {
|
||||
+ t.Errorf("largetext mismatch: got size: %v, expected size: %v", len(g), len(largeTextValue))
|
||||
+ }
|
||||
+ f.RemoveAll()
|
||||
+ if maxMemory < failWhenMaxMemoryLessThan {
|
||||
+ t.Errorf("ReadForm(%v): no error, expect to hit memory limit when maxMemory < %v", maxMemory, failWhenMaxMemoryLessThan)
|
||||
+ }
|
||||
+ return
|
||||
}
|
||||
+ t.Errorf("ReadForm(x) failed for x < 1024, expect success")
|
||||
}
|
||||
|
||||
// TestReadForm_MetadataTooLarge verifies that we account for the size of field names,
|
||||
diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go
|
||||
index 9a21777df8be0..c1284fde25eb7 100644
|
||||
--- a/src/net/textproto/reader.go
|
||||
+++ b/src/net/textproto/reader.go
|
||||
@@ -503,6 +503,12 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
|
||||
|
||||
m := make(MIMEHeader, hint)
|
||||
|
||||
+ // Account for 400 bytes of overhead for the MIMEHeader, plus 200 bytes per entry.
|
||||
+ // Benchmarking map creation as of go1.20, a one-entry MIMEHeader is 416 bytes and large
|
||||
+ // MIMEHeaders average about 200 bytes per entry.
|
||||
+ lim -= 400
|
||||
+ const mapEntryOverhead = 200
|
||||
+
|
||||
// The first line cannot start with a leading space.
|
||||
if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') {
|
||||
line, err := r.readLineSlice()
|
||||
@@ -538,7 +544,7 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
|
||||
vv := m[key]
|
||||
if vv == nil {
|
||||
lim -= int64(len(key))
|
||||
- lim -= 100 // map entry overhead
|
||||
+ lim -= mapEntryOverhead
|
||||
}
|
||||
lim -= int64(len(value))
|
||||
if lim < 0 {
|
||||
349
meta/recipes-devtools/go/go-1.14/CVE-2023-24536_3.patch
Normal file
349
meta/recipes-devtools/go/go-1.14/CVE-2023-24536_3.patch
Normal file
@@ -0,0 +1,349 @@
|
||||
From 7917b5f31204528ea72e0629f0b7d52b35b27538 Mon Sep 17 00:00:00 2001
|
||||
From: Damien Neil <dneil@google.com>
|
||||
Date: Mon, 20 Mar 2023 10:43:19 -0700
|
||||
Subject: [PATCH] [release-branch.go1.19] mime/multipart: limit parsed mime message sizes
|
||||
|
||||
The parsed forms of MIME headers and multipart forms can consume
|
||||
substantially more memory than the size of the input data.
|
||||
A malicious input containing a very large number of headers or
|
||||
form parts can cause excessively large memory allocations.
|
||||
|
||||
Set limits on the size of MIME data:
|
||||
|
||||
Reader.NextPart and Reader.NextRawPart limit the the number
|
||||
of headers in a part to 10000.
|
||||
|
||||
Reader.ReadForm limits the total number of headers in all
|
||||
FileHeaders to 10000.
|
||||
|
||||
Both of these limits may be set with with
|
||||
GODEBUG=multipartmaxheaders=<values>.
|
||||
|
||||
Reader.ReadForm limits the number of parts in a form to 1000.
|
||||
This limit may be set with GODEBUG=multipartmaxparts=<value>.
|
||||
|
||||
Thanks for Jakob Ackermann (@das7pad) for reporting this issue.
|
||||
|
||||
For CVE-2023-24536
|
||||
For #59153
|
||||
For #59269
|
||||
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1802455
|
||||
Run-TryBot: Damien Neil <dneil@google.com>
|
||||
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-by: Julie Qiu <julieqiu@google.com>
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1801087
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Run-TryBot: Roland Shoemaker <bracewell@google.com>
|
||||
Change-Id: If134890d75f0d95c681d67234daf191ba08e6424
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/481985
|
||||
Run-TryBot: Michael Knyszek <mknyszek@google.com>
|
||||
Auto-Submit: Michael Knyszek <mknyszek@google.com>
|
||||
TryBot-Result: Gopher Robot <gobot@golang.org>
|
||||
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/7917b5f31204528ea72e0629f0b7d52b35b27538]
|
||||
CVE: CVE-2023-24536
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
src/mime/multipart/formdata.go | 19 ++++++++-
|
||||
src/mime/multipart/formdata_test.go | 61 ++++++++++++++++++++++++++++
|
||||
src/mime/multipart/multipart.go | 31 ++++++++++----
|
||||
src/mime/multipart/readmimeheader.go | 2 +-
|
||||
src/net/textproto/reader.go | 19 +++++----
|
||||
5 files changed, 115 insertions(+), 17 deletions(-)
|
||||
|
||||
diff --git a/src/mime/multipart/formdata.go b/src/mime/multipart/formdata.go
|
||||
index 216cccb..0b508ae 100644
|
||||
--- a/src/mime/multipart/formdata.go
|
||||
+++ b/src/mime/multipart/formdata.go
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"math"
|
||||
"net/textproto"
|
||||
"os"
|
||||
+ "strconv"
|
||||
)
|
||||
|
||||
// ErrMessageTooLarge is returned by ReadForm if the message form
|
||||
@@ -42,6 +43,15 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
numDiskFiles := 0
|
||||
multipartFiles := godebug.Get("multipartfiles")
|
||||
combineFiles := multipartFiles != "distinct"
|
||||
+ maxParts := 1000
|
||||
+ multipartMaxParts := godebug.Get("multipartmaxparts")
|
||||
+ if multipartMaxParts != "" {
|
||||
+ if v, err := strconv.Atoi(multipartMaxParts); err == nil && v >= 0 {
|
||||
+ maxParts = v
|
||||
+ }
|
||||
+ }
|
||||
+ maxHeaders := maxMIMEHeaders()
|
||||
+
|
||||
defer func() {
|
||||
if file != nil {
|
||||
if cerr := file.Close(); err == nil {
|
||||
@@ -87,13 +97,17 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
}
|
||||
var copyBuf []byte
|
||||
for {
|
||||
- p, err := r.nextPart(false, maxMemoryBytes)
|
||||
+ p, err := r.nextPart(false, maxMemoryBytes, maxHeaders)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
+ if maxParts <= 0 {
|
||||
+ return nil, ErrMessageTooLarge
|
||||
+ }
|
||||
+ maxParts--
|
||||
|
||||
name := p.FormName()
|
||||
if name == "" {
|
||||
@@ -137,6 +151,9 @@ func (r *Reader) readForm(maxMemory int64) (_ *Form, err error) {
|
||||
if maxMemoryBytes < 0 {
|
||||
return nil, ErrMessageTooLarge
|
||||
}
|
||||
+ for _, v := range p.Header {
|
||||
+ maxHeaders -= int64(len(v))
|
||||
+ }
|
||||
fh := &FileHeader{
|
||||
Filename: filename,
|
||||
Header: p.Header,
|
||||
diff --git a/src/mime/multipart/formdata_test.go b/src/mime/multipart/formdata_test.go
|
||||
index 8ed26e0..c78eeb7 100644
|
||||
--- a/src/mime/multipart/formdata_test.go
|
||||
+++ b/src/mime/multipart/formdata_test.go
|
||||
@@ -360,6 +360,67 @@ func testReadFormManyFiles(t *testing.T, distinct bool) {
|
||||
}
|
||||
}
|
||||
|
||||
+func TestReadFormLimits(t *testing.T) {
|
||||
+ for _, test := range []struct {
|
||||
+ values int
|
||||
+ files int
|
||||
+ extraKeysPerFile int
|
||||
+ wantErr error
|
||||
+ godebug string
|
||||
+ }{
|
||||
+ {values: 1000},
|
||||
+ {values: 1001, wantErr: ErrMessageTooLarge},
|
||||
+ {values: 500, files: 500},
|
||||
+ {values: 501, files: 500, wantErr: ErrMessageTooLarge},
|
||||
+ {files: 1000},
|
||||
+ {files: 1001, wantErr: ErrMessageTooLarge},
|
||||
+ {files: 1, extraKeysPerFile: 9998}, // plus Content-Disposition and Content-Type
|
||||
+ {files: 1, extraKeysPerFile: 10000, wantErr: ErrMessageTooLarge},
|
||||
+ {godebug: "multipartmaxparts=100", values: 100},
|
||||
+ {godebug: "multipartmaxparts=100", values: 101, wantErr: ErrMessageTooLarge},
|
||||
+ {godebug: "multipartmaxheaders=100", files: 2, extraKeysPerFile: 48},
|
||||
+ {godebug: "multipartmaxheaders=100", files: 2, extraKeysPerFile: 50, wantErr: ErrMessageTooLarge},
|
||||
+ } {
|
||||
+ name := fmt.Sprintf("values=%v/files=%v/extraKeysPerFile=%v", test.values, test.files, test.extraKeysPerFile)
|
||||
+ if test.godebug != "" {
|
||||
+ name += fmt.Sprintf("/godebug=%v", test.godebug)
|
||||
+ }
|
||||
+ t.Run(name, func(t *testing.T) {
|
||||
+ if test.godebug != "" {
|
||||
+ t.Setenv("GODEBUG", test.godebug)
|
||||
+ }
|
||||
+ var buf bytes.Buffer
|
||||
+ fw := NewWriter(&buf)
|
||||
+ for i := 0; i < test.values; i++ {
|
||||
+ w, _ := fw.CreateFormField(fmt.Sprintf("field%v", i))
|
||||
+ fmt.Fprintf(w, "value %v", i)
|
||||
+ }
|
||||
+ for i := 0; i < test.files; i++ {
|
||||
+ h := make(textproto.MIMEHeader)
|
||||
+ h.Set("Content-Disposition",
|
||||
+ fmt.Sprintf(`form-data; name="file%v"; filename="file%v"`, i, i))
|
||||
+ h.Set("Content-Type", "application/octet-stream")
|
||||
+ for j := 0; j < test.extraKeysPerFile; j++ {
|
||||
+ h.Set(fmt.Sprintf("k%v", j), "v")
|
||||
+ }
|
||||
+ w, _ := fw.CreatePart(h)
|
||||
+ fmt.Fprintf(w, "value %v", i)
|
||||
+ }
|
||||
+ if err := fw.Close(); err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ fr := NewReader(bytes.NewReader(buf.Bytes()), fw.Boundary())
|
||||
+ form, err := fr.ReadForm(1 << 10)
|
||||
+ if err == nil {
|
||||
+ defer form.RemoveAll()
|
||||
+ }
|
||||
+ if err != test.wantErr {
|
||||
+ t.Errorf("ReadForm = %v, want %v", err, test.wantErr)
|
||||
+ }
|
||||
+ })
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
func BenchmarkReadForm(b *testing.B) {
|
||||
for _, test := range []struct {
|
||||
name string
|
||||
diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go
|
||||
index 958cef8..94464a8 100644
|
||||
--- a/src/mime/multipart/multipart.go
|
||||
+++ b/src/mime/multipart/multipart.go
|
||||
@@ -16,11 +16,13 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
+ "internal/godebug"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
"mime/quotedprintable"
|
||||
"net/textproto"
|
||||
+ "strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -121,12 +123,12 @@ func (r *stickyErrorReader) Read(p []byte) (n int, _ error) {
|
||||
return n, r.err
|
||||
}
|
||||
|
||||
-func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize int64) (*Part, error) {
|
||||
+func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize, maxMIMEHeaders int64) (*Part, error) {
|
||||
bp := &Part{
|
||||
Header: make(map[string][]string),
|
||||
mr: mr,
|
||||
}
|
||||
- if err := bp.populateHeaders(maxMIMEHeaderSize); err != nil {
|
||||
+ if err := bp.populateHeaders(maxMIMEHeaderSize, maxMIMEHeaders); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bp.r = partReader{bp}
|
||||
@@ -142,9 +144,9 @@ func newPart(mr *Reader, rawPart bool, maxMIMEHeaderSize int64) (*Part, error) {
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
-func (bp *Part) populateHeaders(maxMIMEHeaderSize int64) error {
|
||||
+func (bp *Part) populateHeaders(maxMIMEHeaderSize, maxMIMEHeaders int64) error {
|
||||
r := textproto.NewReader(bp.mr.bufReader)
|
||||
- header, err := readMIMEHeader(r, maxMIMEHeaderSize)
|
||||
+ header, err := readMIMEHeader(r, maxMIMEHeaderSize, maxMIMEHeaders)
|
||||
if err == nil {
|
||||
bp.Header = header
|
||||
}
|
||||
@@ -306,6 +308,19 @@ type Reader struct {
|
||||
// including header keys, values, and map overhead.
|
||||
const maxMIMEHeaderSize = 10 << 20
|
||||
|
||||
+func maxMIMEHeaders() int64 {
|
||||
+ // multipartMaxHeaders is the maximum number of header entries NextPart will return,
|
||||
+ // as well as the maximum combined total of header entries Reader.ReadForm will return
|
||||
+ // in FileHeaders.
|
||||
+ multipartMaxHeaders := godebug.Get("multipartmaxheaders")
|
||||
+ if multipartMaxHeaders != "" {
|
||||
+ if v, err := strconv.ParseInt(multipartMaxHeaders, 10, 64); err == nil && v >= 0 {
|
||||
+ return v
|
||||
+ }
|
||||
+ }
|
||||
+ return 10000
|
||||
+}
|
||||
+
|
||||
// NextPart returns the next part in the multipart or an error.
|
||||
// When there are no more parts, the error io.EOF is returned.
|
||||
//
|
||||
@@ -313,7 +328,7 @@ const maxMIMEHeaderSize = 10 << 20
|
||||
// has a value of "quoted-printable", that header is instead
|
||||
// hidden and the body is transparently decoded during Read calls.
|
||||
func (r *Reader) NextPart() (*Part, error) {
|
||||
- return r.nextPart(false, maxMIMEHeaderSize)
|
||||
+ return r.nextPart(false, maxMIMEHeaderSize, maxMIMEHeaders())
|
||||
}
|
||||
|
||||
// NextRawPart returns the next part in the multipart or an error.
|
||||
@@ -322,10 +337,10 @@ func (r *Reader) NextPart() (*Part, error) {
|
||||
// Unlike NextPart, it does not have special handling for
|
||||
// "Content-Transfer-Encoding: quoted-printable".
|
||||
func (r *Reader) NextRawPart() (*Part, error) {
|
||||
- return r.nextPart(true, maxMIMEHeaderSize)
|
||||
+ return r.nextPart(true, maxMIMEHeaderSize, maxMIMEHeaders())
|
||||
}
|
||||
|
||||
-func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize int64) (*Part, error) {
|
||||
+func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize, maxMIMEHeaders int64) (*Part, error) {
|
||||
if r.currentPart != nil {
|
||||
r.currentPart.Close()
|
||||
}
|
||||
@@ -350,7 +365,7 @@ func (r *Reader) nextPart(rawPart bool, maxMIMEHeaderSize int64) (*Part, error)
|
||||
|
||||
if r.isBoundaryDelimiterLine(line) {
|
||||
r.partsRead++
|
||||
- bp, err := newPart(r, rawPart, maxMIMEHeaderSize)
|
||||
+ bp, err := newPart(r, rawPart, maxMIMEHeaderSize, maxMIMEHeaders)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
diff --git a/src/mime/multipart/readmimeheader.go b/src/mime/multipart/readmimeheader.go
|
||||
index 6836928..25aa6e2 100644
|
||||
--- a/src/mime/multipart/readmimeheader.go
|
||||
+++ b/src/mime/multipart/readmimeheader.go
|
||||
@@ -11,4 +11,4 @@ import (
|
||||
// readMIMEHeader is defined in package net/textproto.
|
||||
//
|
||||
//go:linkname readMIMEHeader net/textproto.readMIMEHeader
|
||||
-func readMIMEHeader(r *textproto.Reader, lim int64) (textproto.MIMEHeader, error)
|
||||
+func readMIMEHeader(r *textproto.Reader, maxMemory, maxHeaders int64) (textproto.MIMEHeader, error)
|
||||
diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go
|
||||
index 1c79f0a..ad2d777 100644
|
||||
--- a/src/net/textproto/reader.go
|
||||
+++ b/src/net/textproto/reader.go
|
||||
@@ -484,12 +484,12 @@ func (r *Reader) ReadDotLines() ([]string, error) {
|
||||
// }
|
||||
//
|
||||
func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
|
||||
- return readMIMEHeader(r, math.MaxInt64)
|
||||
+ return readMIMEHeader(r, math.MaxInt64, math.MaxInt64)
|
||||
}
|
||||
|
||||
// readMIMEHeader is a version of ReadMIMEHeader which takes a limit on the header size.
|
||||
// It is called by the mime/multipart package.
|
||||
-func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
|
||||
+func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) {
|
||||
// Avoid lots of small slice allocations later by allocating one
|
||||
// large one ahead of time which we'll cut up into smaller
|
||||
// slices. If this isn't big enough later, we allocate small ones.
|
||||
@@ -507,7 +507,7 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
|
||||
// Account for 400 bytes of overhead for the MIMEHeader, plus 200 bytes per entry.
|
||||
// Benchmarking map creation as of go1.20, a one-entry MIMEHeader is 416 bytes and large
|
||||
// MIMEHeaders average about 200 bytes per entry.
|
||||
- lim -= 400
|
||||
+ maxMemory -= 400
|
||||
const mapEntryOverhead = 200
|
||||
|
||||
// The first line cannot start with a leading space.
|
||||
@@ -539,6 +539,11 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
+ maxHeaders--
|
||||
+ if maxHeaders < 0 {
|
||||
+ return nil, errors.New("message too large")
|
||||
+ }
|
||||
+
|
||||
// backport 5c55ac9bf1e5f779220294c843526536605f42ab
|
||||
//
|
||||
// value is computed as
|
||||
@@ -557,11 +562,11 @@ func readMIMEHeader(r *Reader, lim int64) (MIMEHeader, error) {
|
||||
|
||||
vv := m[key]
|
||||
if vv == nil {
|
||||
- lim -= int64(len(key))
|
||||
- lim -= mapEntryOverhead
|
||||
+ maxMemory -= int64(len(key))
|
||||
+ maxMemory -= mapEntryOverhead
|
||||
}
|
||||
- lim -= int64(len(value))
|
||||
- if lim < 0 {
|
||||
+ maxMemory -= int64(len(value))
|
||||
+ if maxMemory < 0 {
|
||||
// TODO: This should be a distinguishable error (ErrMessageTooLarge)
|
||||
// to allow mime/multipart to detect it.
|
||||
return m, errors.New("message too large")
|
||||
--
|
||||
2.25.1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 8acd01094d9ee17f6e763a61e49a8a808b3a9ddb Mon Sep 17 00:00:00 2001
|
||||
From: Brad Fitzpatrick <bradfitz@golang.org>
|
||||
Date: Mon, 2 Aug 2021 14:55:51 -0700
|
||||
Subject: [PATCH 1/3] net/netip: add new IP address package
|
||||
Subject: [PATCH 1/6] net/netip: add new IP address package
|
||||
|
||||
Co-authored-by: Alex Willmer <alex@moreati.org.uk> (GitHub @moreati)
|
||||
Co-authored-by: Alexander Yastrebov <yastrebov.alex@gmail.com>
|
||||
@@ -31,7 +31,7 @@ Trust: Brad Fitzpatrick <bradfitz@golang.org>
|
||||
|
||||
Dependency Patch #1
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/a59e33224e42d60a97fa720a45e1b74eb6aaa3d0]
|
||||
Upstream-Status: Backport from https://github.com/golang/go/commit/a59e33224e42d60a97fa720a45e1b74eb6aaa3d0
|
||||
CVE: CVE-2023-24538
|
||||
Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
|
||||
---
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
From 6fc21505614f36178df0dad7034b6b8e3f7588d5 Mon Sep 17 00:00:00 2001
|
||||
From: empijei <robclap8@gmail.com>
|
||||
Date: Fri, 27 Mar 2020 19:27:55 +0100
|
||||
Subject: [PATCH 2/3] html/template,text/template: switch to Unicode escapes
|
||||
Subject: [PATCH 2/6] html/template,text/template: switch to Unicode escapes
|
||||
for JSON compatibility
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
@@ -31,10 +31,238 @@ Upstream-Status: Backport from https://github.com/golang/go/commit/d4d298040d072
|
||||
CVE: CVE-2023-24538
|
||||
Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
|
||||
---
|
||||
src/html/template/js.go | 70 +++++++++++++++++++++++++++-------------------
|
||||
src/text/template/funcs.go | 8 +++---
|
||||
2 files changed, 46 insertions(+), 32 deletions(-)
|
||||
src/html/template/content_test.go | 70 +++++++++++++++++++-------------------
|
||||
src/html/template/escape_test.go | 6 ++--
|
||||
src/html/template/example_test.go | 6 ++--
|
||||
src/html/template/js.go | 70 +++++++++++++++++++++++---------------
|
||||
src/html/template/js_test.go | 68 ++++++++++++++++++------------------
|
||||
src/html/template/template_test.go | 39 +++++++++++++++++++++
|
||||
src/text/template/exec_test.go | 6 ++--
|
||||
src/text/template/funcs.go | 8 ++---
|
||||
8 files changed, 163 insertions(+), 110 deletions(-)
|
||||
|
||||
diff --git a/src/html/template/content_test.go b/src/html/template/content_test.go
|
||||
index 72d56f5..bd86527 100644
|
||||
--- a/src/html/template/content_test.go
|
||||
+++ b/src/html/template/content_test.go
|
||||
@@ -18,7 +18,7 @@ func TestTypedContent(t *testing.T) {
|
||||
HTML(`Hello, <b>World</b> &tc!`),
|
||||
HTMLAttr(` dir="ltr"`),
|
||||
JS(`c && alert("Hello, World!");`),
|
||||
- JSStr(`Hello, World & O'Reilly\x21`),
|
||||
+ JSStr(`Hello, World & O'Reilly\u0021`),
|
||||
URL(`greeting=H%69,&addressee=(World)`),
|
||||
Srcset(`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`),
|
||||
URL(`,foo/,`),
|
||||
@@ -70,7 +70,7 @@ func TestTypedContent(t *testing.T) {
|
||||
`Hello, <b>World</b> &tc!`,
|
||||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
- `Hello, World & O'Reilly\x21`,
|
||||
+ `Hello, World & O'Reilly\u0021`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`,foo/,`,
|
||||
@@ -100,7 +100,7 @@ func TestTypedContent(t *testing.T) {
|
||||
`Hello, World &tc!`,
|
||||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
- `Hello, World & O'Reilly\x21`,
|
||||
+ `Hello, World & O'Reilly\u0021`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`,foo/,`,
|
||||
@@ -115,7 +115,7 @@ func TestTypedContent(t *testing.T) {
|
||||
`Hello, World &tc!`,
|
||||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
- `Hello, World & O'Reilly\x21`,
|
||||
+ `Hello, World & O'Reilly\u0021`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`,foo/,`,
|
||||
@@ -130,7 +130,7 @@ func TestTypedContent(t *testing.T) {
|
||||
`Hello, <b>World</b> &tc!`,
|
||||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
- `Hello, World & O'Reilly\x21`,
|
||||
+ `Hello, World & O'Reilly\u0021`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`,foo/,`,
|
||||
@@ -146,7 +146,7 @@ func TestTypedContent(t *testing.T) {
|
||||
// Not escaped.
|
||||
`c && alert("Hello, World!");`,
|
||||
// Escape sequence not over-escaped.
|
||||
- `"Hello, World & O'Reilly\x21"`,
|
||||
+ `"Hello, World & O'Reilly\u0021"`,
|
||||
`"greeting=H%69,\u0026addressee=(World)"`,
|
||||
`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
|
||||
`",foo/,"`,
|
||||
@@ -162,7 +162,7 @@ func TestTypedContent(t *testing.T) {
|
||||
// Not JS escaped but HTML escaped.
|
||||
`c && alert("Hello, World!");`,
|
||||
// Escape sequence not over-escaped.
|
||||
- `"Hello, World & O'Reilly\x21"`,
|
||||
+ `"Hello, World & O'Reilly\u0021"`,
|
||||
`"greeting=H%69,\u0026addressee=(World)"`,
|
||||
`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
|
||||
`",foo/,"`,
|
||||
@@ -171,30 +171,30 @@ func TestTypedContent(t *testing.T) {
|
||||
{
|
||||
`<script>alert("{{.}}")</script>`,
|
||||
[]string{
|
||||
- `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`,
|
||||
- `a[href =~ \x22\/\/example.com\x22]#foo`,
|
||||
- `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`,
|
||||
- ` dir=\x22ltr\x22`,
|
||||
- `c \x26\x26 alert(\x22Hello, World!\x22);`,
|
||||
+ `\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`,
|
||||
+ `a[href =~ \u0022\/\/example.com\u0022]#foo`,
|
||||
+ `Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`,
|
||||
+ ` dir=\u0022ltr\u0022`,
|
||||
+ `c \u0026\u0026 alert(\u0022Hello, World!\u0022);`,
|
||||
// Escape sequence not over-escaped.
|
||||
- `Hello, World \x26 O\x27Reilly\x21`,
|
||||
- `greeting=H%69,\x26addressee=(World)`,
|
||||
- `greeting=H%69,\x26addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
|
||||
+ `Hello, World \u0026 O\u0027Reilly\u0021`,
|
||||
+ `greeting=H%69,\u0026addressee=(World)`,
|
||||
+ `greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
|
||||
`,foo\/,`,
|
||||
},
|
||||
},
|
||||
{
|
||||
`<script type="text/javascript">alert("{{.}}")</script>`,
|
||||
[]string{
|
||||
- `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`,
|
||||
- `a[href =~ \x22\/\/example.com\x22]#foo`,
|
||||
- `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`,
|
||||
- ` dir=\x22ltr\x22`,
|
||||
- `c \x26\x26 alert(\x22Hello, World!\x22);`,
|
||||
+ `\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`,
|
||||
+ `a[href =~ \u0022\/\/example.com\u0022]#foo`,
|
||||
+ `Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`,
|
||||
+ ` dir=\u0022ltr\u0022`,
|
||||
+ `c \u0026\u0026 alert(\u0022Hello, World!\u0022);`,
|
||||
// Escape sequence not over-escaped.
|
||||
- `Hello, World \x26 O\x27Reilly\x21`,
|
||||
- `greeting=H%69,\x26addressee=(World)`,
|
||||
- `greeting=H%69,\x26addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
|
||||
+ `Hello, World \u0026 O\u0027Reilly\u0021`,
|
||||
+ `greeting=H%69,\u0026addressee=(World)`,
|
||||
+ `greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
|
||||
`,foo\/,`,
|
||||
},
|
||||
},
|
||||
@@ -208,7 +208,7 @@ func TestTypedContent(t *testing.T) {
|
||||
// Not escaped.
|
||||
`c && alert("Hello, World!");`,
|
||||
// Escape sequence not over-escaped.
|
||||
- `"Hello, World & O'Reilly\x21"`,
|
||||
+ `"Hello, World & O'Reilly\u0021"`,
|
||||
`"greeting=H%69,\u0026addressee=(World)"`,
|
||||
`"greeting=H%69,\u0026addressee=(World) 2x, https://golang.org/favicon.ico 500.5w"`,
|
||||
`",foo/,"`,
|
||||
@@ -224,7 +224,7 @@ func TestTypedContent(t *testing.T) {
|
||||
`Hello, <b>World</b> &tc!`,
|
||||
` dir="ltr"`,
|
||||
`c && alert("Hello, World!");`,
|
||||
- `Hello, World & O'Reilly\x21`,
|
||||
+ `Hello, World & O'Reilly\u0021`,
|
||||
`greeting=H%69,&addressee=(World)`,
|
||||
`greeting=H%69,&addressee=(World) 2x, https://golang.org/favicon.ico 500.5w`,
|
||||
`,foo/,`,
|
||||
@@ -233,15 +233,15 @@ func TestTypedContent(t *testing.T) {
|
||||
{
|
||||
`<button onclick='alert("{{.}}")'>`,
|
||||
[]string{
|
||||
- `\x3cb\x3e \x22foo%\x22 O\x27Reilly \x26bar;`,
|
||||
- `a[href =~ \x22\/\/example.com\x22]#foo`,
|
||||
- `Hello, \x3cb\x3eWorld\x3c\/b\x3e \x26amp;tc!`,
|
||||
- ` dir=\x22ltr\x22`,
|
||||
- `c \x26\x26 alert(\x22Hello, World!\x22);`,
|
||||
+ `\u003cb\u003e \u0022foo%\u0022 O\u0027Reilly \u0026bar;`,
|
||||
+ `a[href =~ \u0022\/\/example.com\u0022]#foo`,
|
||||
+ `Hello, \u003cb\u003eWorld\u003c\/b\u003e \u0026amp;tc!`,
|
||||
+ ` dir=\u0022ltr\u0022`,
|
||||
+ `c \u0026\u0026 alert(\u0022Hello, World!\u0022);`,
|
||||
// Escape sequence not over-escaped.
|
||||
- `Hello, World \x26 O\x27Reilly\x21`,
|
||||
- `greeting=H%69,\x26addressee=(World)`,
|
||||
- `greeting=H%69,\x26addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
|
||||
+ `Hello, World \u0026 O\u0027Reilly\u0021`,
|
||||
+ `greeting=H%69,\u0026addressee=(World)`,
|
||||
+ `greeting=H%69,\u0026addressee=(World) 2x, https:\/\/golang.org\/favicon.ico 500.5w`,
|
||||
`,foo\/,`,
|
||||
},
|
||||
},
|
||||
@@ -253,7 +253,7 @@ func TestTypedContent(t *testing.T) {
|
||||
`Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`,
|
||||
`%20dir%3d%22ltr%22`,
|
||||
`c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
|
||||
- `Hello%2c%20World%20%26%20O%27Reilly%5cx21`,
|
||||
+ `Hello%2c%20World%20%26%20O%27Reilly%5cu0021`,
|
||||
// Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is done.
|
||||
`greeting=H%69,&addressee=%28World%29`,
|
||||
`greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
|
||||
@@ -268,7 +268,7 @@ func TestTypedContent(t *testing.T) {
|
||||
`Hello%2c%20%3cb%3eWorld%3c%2fb%3e%20%26amp%3btc%21`,
|
||||
`%20dir%3d%22ltr%22`,
|
||||
`c%20%26%26%20alert%28%22Hello%2c%20World%21%22%29%3b`,
|
||||
- `Hello%2c%20World%20%26%20O%27Reilly%5cx21`,
|
||||
+ `Hello%2c%20World%20%26%20O%27Reilly%5cu0021`,
|
||||
// Quotes and parens are escaped but %69 is not over-escaped. HTML escaping is not done.
|
||||
`greeting=H%69,&addressee=%28World%29`,
|
||||
`greeting%3dH%2569%2c%26addressee%3d%28World%29%202x%2c%20https%3a%2f%2fgolang.org%2ffavicon.ico%20500.5w`,
|
||||
diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
|
||||
index e72a9ba..c709660 100644
|
||||
--- a/src/html/template/escape_test.go
|
||||
+++ b/src/html/template/escape_test.go
|
||||
@@ -238,7 +238,7 @@ func TestEscape(t *testing.T) {
|
||||
{
|
||||
"jsStr",
|
||||
"<button onclick='alert("{{.H}}")'>",
|
||||
- `<button onclick='alert("\x3cHello\x3e")'>`,
|
||||
+ `<button onclick='alert("\u003cHello\u003e")'>`,
|
||||
},
|
||||
{
|
||||
"badMarshaler",
|
||||
@@ -259,7 +259,7 @@ func TestEscape(t *testing.T) {
|
||||
{
|
||||
"jsRe",
|
||||
`<button onclick='alert(/{{"foo+bar"}}/.test(""))'>`,
|
||||
- `<button onclick='alert(/foo\x2bbar/.test(""))'>`,
|
||||
+ `<button onclick='alert(/foo\u002bbar/.test(""))'>`,
|
||||
},
|
||||
{
|
||||
"jsReBlank",
|
||||
@@ -825,7 +825,7 @@ func TestEscapeSet(t *testing.T) {
|
||||
"main": `<button onclick="title='{{template "helper"}}'; ...">{{template "helper"}}</button>`,
|
||||
"helper": `{{11}} of {{"<100>"}}`,
|
||||
},
|
||||
- `<button onclick="title='11 of \x3c100\x3e'; ...">11 of <100></button>`,
|
||||
+ `<button onclick="title='11 of \u003c100\u003e'; ...">11 of <100></button>`,
|
||||
},
|
||||
// A non-recursive template that ends in a different context.
|
||||
// helper starts in jsCtxRegexp and ends in jsCtxDivOp.
|
||||
diff --git a/src/html/template/example_test.go b/src/html/template/example_test.go
|
||||
index 9d965f1..6cf936f 100644
|
||||
--- a/src/html/template/example_test.go
|
||||
+++ b/src/html/template/example_test.go
|
||||
@@ -116,9 +116,9 @@ func Example_escape() {
|
||||
// "Fran & Freddie's Diner" <tasty@example.com>
|
||||
// "Fran & Freddie's Diner" <tasty@example.com>
|
||||
// "Fran & Freddie's Diner"32<tasty@example.com>
|
||||
- // \"Fran \x26 Freddie\'s Diner\" \x3Ctasty@example.com\x3E
|
||||
- // \"Fran \x26 Freddie\'s Diner\" \x3Ctasty@example.com\x3E
|
||||
- // \"Fran \x26 Freddie\'s Diner\"32\x3Ctasty@example.com\x3E
|
||||
+ // \"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E
|
||||
+ // \"Fran \u0026 Freddie\'s Diner\" \u003Ctasty@example.com\u003E
|
||||
+ // \"Fran \u0026 Freddie\'s Diner\"32\u003Ctasty@example.com\u003E
|
||||
// %22Fran+%26+Freddie%27s+Diner%2232%3Ctasty%40example.com%3E
|
||||
|
||||
}
|
||||
diff --git a/src/html/template/js.go b/src/html/template/js.go
|
||||
index 0e91458..ea9c183 100644
|
||||
--- a/src/html/template/js.go
|
||||
@@ -173,6 +401,217 @@ index 0e91458..ea9c183 100644
|
||||
'?': `\?`,
|
||||
'[': `\[`,
|
||||
'\\': `\\`,
|
||||
diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go
|
||||
index 075adaa..d7ee47b 100644
|
||||
--- a/src/html/template/js_test.go
|
||||
+++ b/src/html/template/js_test.go
|
||||
@@ -137,7 +137,7 @@ func TestJSValEscaper(t *testing.T) {
|
||||
{"foo", `"foo"`},
|
||||
// Newlines.
|
||||
{"\r\n\u2028\u2029", `"\r\n\u2028\u2029"`},
|
||||
- // "\v" == "v" on IE 6 so use "\x0b" instead.
|
||||
+ // "\v" == "v" on IE 6 so use "\u000b" instead.
|
||||
{"\t\x0b", `"\t\u000b"`},
|
||||
{struct{ X, Y int }{1, 2}, `{"X":1,"Y":2}`},
|
||||
{[]interface{}{}, "[]"},
|
||||
@@ -173,7 +173,7 @@ func TestJSStrEscaper(t *testing.T) {
|
||||
}{
|
||||
{"", ``},
|
||||
{"foo", `foo`},
|
||||
- {"\u0000", `\0`},
|
||||
+ {"\u0000", `\u0000`},
|
||||
{"\t", `\t`},
|
||||
{"\n", `\n`},
|
||||
{"\r", `\r`},
|
||||
@@ -183,14 +183,14 @@ func TestJSStrEscaper(t *testing.T) {
|
||||
{"\\n", `\\n`},
|
||||
{"foo\r\nbar", `foo\r\nbar`},
|
||||
// Preserve attribute boundaries.
|
||||
- {`"`, `\x22`},
|
||||
- {`'`, `\x27`},
|
||||
+ {`"`, `\u0022`},
|
||||
+ {`'`, `\u0027`},
|
||||
// Allow embedding in HTML without further escaping.
|
||||
- {`&`, `\x26amp;`},
|
||||
+ {`&`, `\u0026amp;`},
|
||||
// Prevent breaking out of text node and element boundaries.
|
||||
- {"</script>", `\x3c\/script\x3e`},
|
||||
- {"<![CDATA[", `\x3c![CDATA[`},
|
||||
- {"]]>", `]]\x3e`},
|
||||
+ {"</script>", `\u003c\/script\u003e`},
|
||||
+ {"<![CDATA[", `\u003c![CDATA[`},
|
||||
+ {"]]>", `]]\u003e`},
|
||||
// https://dev.w3.org/html5/markup/aria/syntax.html#escaping-text-span
|
||||
// "The text in style, script, title, and textarea elements
|
||||
// must not have an escaping text span start that is not
|
||||
@@ -201,11 +201,11 @@ func TestJSStrEscaper(t *testing.T) {
|
||||
// allow regular text content to be interpreted as script
|
||||
// allowing script execution via a combination of a JS string
|
||||
// injection followed by an HTML text injection.
|
||||
- {"<!--", `\x3c!--`},
|
||||
- {"-->", `--\x3e`},
|
||||
+ {"<!--", `\u003c!--`},
|
||||
+ {"-->", `--\u003e`},
|
||||
// From https://code.google.com/p/doctype/wiki/ArticleUtf7
|
||||
{"+ADw-script+AD4-alert(1)+ADw-/script+AD4-",
|
||||
- `\x2bADw-script\x2bAD4-alert(1)\x2bADw-\/script\x2bAD4-`,
|
||||
+ `\u002bADw-script\u002bAD4-alert(1)\u002bADw-\/script\u002bAD4-`,
|
||||
},
|
||||
// Invalid UTF-8 sequence
|
||||
{"foo\xA0bar", "foo\xA0bar"},
|
||||
@@ -228,7 +228,7 @@ func TestJSRegexpEscaper(t *testing.T) {
|
||||
}{
|
||||
{"", `(?:)`},
|
||||
{"foo", `foo`},
|
||||
- {"\u0000", `\0`},
|
||||
+ {"\u0000", `\u0000`},
|
||||
{"\t", `\t`},
|
||||
{"\n", `\n`},
|
||||
{"\r", `\r`},
|
||||
@@ -238,19 +238,19 @@ func TestJSRegexpEscaper(t *testing.T) {
|
||||
{"\\n", `\\n`},
|
||||
{"foo\r\nbar", `foo\r\nbar`},
|
||||
// Preserve attribute boundaries.
|
||||
- {`"`, `\x22`},
|
||||
- {`'`, `\x27`},
|
||||
+ {`"`, `\u0022`},
|
||||
+ {`'`, `\u0027`},
|
||||
// Allow embedding in HTML without further escaping.
|
||||
- {`&`, `\x26amp;`},
|
||||
+ {`&`, `\u0026amp;`},
|
||||
// Prevent breaking out of text node and element boundaries.
|
||||
- {"</script>", `\x3c\/script\x3e`},
|
||||
- {"<![CDATA[", `\x3c!\[CDATA\[`},
|
||||
- {"]]>", `\]\]\x3e`},
|
||||
+ {"</script>", `\u003c\/script\u003e`},
|
||||
+ {"<![CDATA[", `\u003c!\[CDATA\[`},
|
||||
+ {"]]>", `\]\]\u003e`},
|
||||
// Escaping text spans.
|
||||
- {"<!--", `\x3c!\-\-`},
|
||||
- {"-->", `\-\-\x3e`},
|
||||
+ {"<!--", `\u003c!\-\-`},
|
||||
+ {"-->", `\-\-\u003e`},
|
||||
{"*", `\*`},
|
||||
- {"+", `\x2b`},
|
||||
+ {"+", `\u002b`},
|
||||
{"?", `\?`},
|
||||
{"[](){}", `\[\]\(\)\{\}`},
|
||||
{"$foo|x.y", `\$foo\|x\.y`},
|
||||
@@ -284,27 +284,27 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
|
||||
{
|
||||
"jsStrEscaper",
|
||||
jsStrEscaper,
|
||||
- "\\0\x01\x02\x03\x04\x05\x06\x07" +
|
||||
- "\x08\\t\\n\\x0b\\f\\r\x0E\x0F" +
|
||||
- "\x10\x11\x12\x13\x14\x15\x16\x17" +
|
||||
- "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
|
||||
- ` !\x22#$%\x26\x27()*\x2b,-.\/` +
|
||||
- `0123456789:;\x3c=\x3e?` +
|
||||
+ `\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007` +
|
||||
+ `\u0008\t\n\u000b\f\r\u000e\u000f` +
|
||||
+ `\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017` +
|
||||
+ `\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f` +
|
||||
+ ` !\u0022#$%\u0026\u0027()*\u002b,-.\/` +
|
||||
+ `0123456789:;\u003c=\u003e?` +
|
||||
`@ABCDEFGHIJKLMNO` +
|
||||
`PQRSTUVWXYZ[\\]^_` +
|
||||
"`abcdefghijklmno" +
|
||||
- "pqrstuvwxyz{|}~\x7f" +
|
||||
+ "pqrstuvwxyz{|}~\u007f" +
|
||||
"\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
|
||||
},
|
||||
{
|
||||
"jsRegexpEscaper",
|
||||
jsRegexpEscaper,
|
||||
- "\\0\x01\x02\x03\x04\x05\x06\x07" +
|
||||
- "\x08\\t\\n\\x0b\\f\\r\x0E\x0F" +
|
||||
- "\x10\x11\x12\x13\x14\x15\x16\x17" +
|
||||
- "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" +
|
||||
- ` !\x22#\$%\x26\x27\(\)\*\x2b,\-\.\/` +
|
||||
- `0123456789:;\x3c=\x3e\?` +
|
||||
+ `\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007` +
|
||||
+ `\u0008\t\n\u000b\f\r\u000e\u000f` +
|
||||
+ `\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017` +
|
||||
+ `\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f` +
|
||||
+ ` !\u0022#\$%\u0026\u0027\(\)\*\u002b,\-\.\/` +
|
||||
+ `0123456789:;\u003c=\u003e\?` +
|
||||
`@ABCDEFGHIJKLMNO` +
|
||||
`PQRSTUVWXYZ\[\\\]\^_` +
|
||||
"`abcdefghijklmno" +
|
||||
diff --git a/src/html/template/template_test.go b/src/html/template/template_test.go
|
||||
index 13e6ba4..86bd4db 100644
|
||||
--- a/src/html/template/template_test.go
|
||||
+++ b/src/html/template/template_test.go
|
||||
@@ -6,6 +6,7 @@ package template_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
+ "encoding/json"
|
||||
. "html/template"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -121,6 +122,44 @@ func TestNumbers(t *testing.T) {
|
||||
c.mustExecute(c.root, nil, "12.34 7.5")
|
||||
}
|
||||
|
||||
+func TestStringsInScriptsWithJsonContentTypeAreCorrectlyEscaped(t *testing.T) {
|
||||
+ // See #33671 and #37634 for more context on this.
|
||||
+ tests := []struct{ name, in string }{
|
||||
+ {"empty", ""},
|
||||
+ {"invalid", string(rune(-1))},
|
||||
+ {"null", "\u0000"},
|
||||
+ {"unit separator", "\u001F"},
|
||||
+ {"tab", "\t"},
|
||||
+ {"gt and lt", "<>"},
|
||||
+ {"quotes", `'"`},
|
||||
+ {"ASCII letters", "ASCII letters"},
|
||||
+ {"Unicode", "ʕ⊙ϖ⊙ʔ"},
|
||||
+ {"Pizza", "P"},
|
||||
+ }
|
||||
+ const (
|
||||
+ prefix = `<script type="application/ld+json">`
|
||||
+ suffix = `</script>`
|
||||
+ templ = prefix + `"{{.}}"` + suffix
|
||||
+ )
|
||||
+ tpl := Must(New("JS string is JSON string").Parse(templ))
|
||||
+ for _, tt := range tests {
|
||||
+ t.Run(tt.name, func(t *testing.T) {
|
||||
+ var buf bytes.Buffer
|
||||
+ if err := tpl.Execute(&buf, tt.in); err != nil {
|
||||
+ t.Fatalf("Cannot render template: %v", err)
|
||||
+ }
|
||||
+ trimmed := bytes.TrimSuffix(bytes.TrimPrefix(buf.Bytes(), []byte(prefix)), []byte(suffix))
|
||||
+ var got string
|
||||
+ if err := json.Unmarshal(trimmed, &got); err != nil {
|
||||
+ t.Fatalf("Cannot parse JS string %q as JSON: %v", trimmed[1:len(trimmed)-1], err)
|
||||
+ }
|
||||
+ if got != tt.in {
|
||||
+ t.Errorf("Serialization changed the string value: got %q want %q", got, tt.in)
|
||||
+ }
|
||||
+ })
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
type testCase struct {
|
||||
t *testing.T
|
||||
root *Template
|
||||
diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go
|
||||
index 77294ed..b8a809e 100644
|
||||
--- a/src/text/template/exec_test.go
|
||||
+++ b/src/text/template/exec_test.go
|
||||
@@ -911,9 +911,9 @@ func TestJSEscaping(t *testing.T) {
|
||||
{`Go "jump" \`, `Go \"jump\" \\`},
|
||||
{`Yukihiro says "今日は世界"`, `Yukihiro says \"今日は世界\"`},
|
||||
{"unprintable \uFDFF", `unprintable \uFDFF`},
|
||||
- {`<html>`, `\x3Chtml\x3E`},
|
||||
- {`no = in attributes`, `no \x3D in attributes`},
|
||||
- {`' does not become HTML entity`, `\x26#x27; does not become HTML entity`},
|
||||
+ {`<html>`, `\u003Chtml\u003E`},
|
||||
+ {`no = in attributes`, `no \u003D in attributes`},
|
||||
+ {`' does not become HTML entity`, `\u0026#x27; does not become HTML entity`},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
s := JSEscapeString(tc.in)
|
||||
diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go
|
||||
index 46125bc..f3de9fb 100644
|
||||
--- a/src/text/template/funcs.go
|
||||
|
||||
393
meta/recipes-devtools/go/go-1.14/CVE-2023-24538_3.patch
Normal file
393
meta/recipes-devtools/go/go-1.14/CVE-2023-24538_3.patch
Normal file
@@ -0,0 +1,393 @@
|
||||
From 7ddce23c7d5b728acf8482f5006497c7b9915f8a Mon Sep 17 00:00:00 2001
|
||||
From: Ariel Mashraki <ariel@mashraki.co.il>
|
||||
Date: Wed, 22 Apr 2020 22:17:56 +0300
|
||||
Subject: [PATCH 3/6] text/template: add CommentNode to template parse tree
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Fixes #34652
|
||||
|
||||
Change-Id: Icf6e3eda593fed826736f34f95a9d66f5450cc98
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/229398
|
||||
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
|
||||
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
|
||||
TryBot-Result: Gobot Gobot <gobot@golang.org>
|
||||
|
||||
Dependency Patch #3
|
||||
|
||||
Upstream-Status: Backport from https://github.com/golang/go/commit/c8ea03828b0645b1fd5725888e44873b75fcfbb6
|
||||
CVE: CVE-2023-24538
|
||||
Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
|
||||
---
|
||||
api/next.txt | 19 +++++++++++++++++++
|
||||
src/html/template/escape.go | 2 ++
|
||||
src/html/template/template_test.go | 16 ++++++++++++++++
|
||||
src/text/template/exec.go | 1 +
|
||||
src/text/template/parse/lex.go | 8 +++++++-
|
||||
src/text/template/parse/lex_test.go | 7 +++++--
|
||||
src/text/template/parse/node.go | 33 +++++++++++++++++++++++++++++++++
|
||||
src/text/template/parse/parse.go | 22 +++++++++++++++++++---
|
||||
src/text/template/parse/parse_test.go | 25 +++++++++++++++++++++++++
|
||||
9 files changed, 127 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/api/next.txt b/api/next.txt
|
||||
index e69de29..076f39e 100644
|
||||
--- a/api/next.txt
|
||||
+++ b/api/next.txt
|
||||
@@ -0,0 +1,19 @@
|
||||
+pkg unicode, const Version = "13.0.0"
|
||||
+pkg unicode, var Chorasmian *RangeTable
|
||||
+pkg unicode, var Dives_Akuru *RangeTable
|
||||
+pkg unicode, var Khitan_Small_Script *RangeTable
|
||||
+pkg unicode, var Yezidi *RangeTable
|
||||
+pkg text/template/parse, const NodeComment = 20
|
||||
+pkg text/template/parse, const NodeComment NodeType
|
||||
+pkg text/template/parse, const ParseComments = 1
|
||||
+pkg text/template/parse, const ParseComments Mode
|
||||
+pkg text/template/parse, method (*CommentNode) Copy() Node
|
||||
+pkg text/template/parse, method (*CommentNode) String() string
|
||||
+pkg text/template/parse, method (CommentNode) Position() Pos
|
||||
+pkg text/template/parse, method (CommentNode) Type() NodeType
|
||||
+pkg text/template/parse, type CommentNode struct
|
||||
+pkg text/template/parse, type CommentNode struct, Text string
|
||||
+pkg text/template/parse, type CommentNode struct, embedded NodeType
|
||||
+pkg text/template/parse, type CommentNode struct, embedded Pos
|
||||
+pkg text/template/parse, type Mode uint
|
||||
+pkg text/template/parse, type Tree struct, Mode Mode
|
||||
diff --git a/src/html/template/escape.go b/src/html/template/escape.go
|
||||
index f12dafa..8739735 100644
|
||||
--- a/src/html/template/escape.go
|
||||
+++ b/src/html/template/escape.go
|
||||
@@ -124,6 +124,8 @@ func (e *escaper) escape(c context, n parse.Node) context {
|
||||
switch n := n.(type) {
|
||||
case *parse.ActionNode:
|
||||
return e.escapeAction(c, n)
|
||||
+ case *parse.CommentNode:
|
||||
+ return c
|
||||
case *parse.IfNode:
|
||||
return e.escapeBranch(c, &n.BranchNode, "if")
|
||||
case *parse.ListNode:
|
||||
diff --git a/src/html/template/template_test.go b/src/html/template/template_test.go
|
||||
index 86bd4db..1f2c888 100644
|
||||
--- a/src/html/template/template_test.go
|
||||
+++ b/src/html/template/template_test.go
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
. "html/template"
|
||||
"strings"
|
||||
"testing"
|
||||
+ "text/template/parse"
|
||||
)
|
||||
|
||||
func TestTemplateClone(t *testing.T) {
|
||||
@@ -160,6 +161,21 @@ func TestStringsInScriptsWithJsonContentTypeAreCorrectlyEscaped(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
+func TestSkipEscapeComments(t *testing.T) {
|
||||
+ c := newTestCase(t)
|
||||
+ tr := parse.New("root")
|
||||
+ tr.Mode = parse.ParseComments
|
||||
+ newT, err := tr.Parse("{{/* A comment */}}{{ 1 }}{{/* Another comment */}}", "", "", make(map[string]*parse.Tree))
|
||||
+ if err != nil {
|
||||
+ t.Fatalf("Cannot parse template text: %v", err)
|
||||
+ }
|
||||
+ c.root, err = c.root.AddParseTree("root", newT)
|
||||
+ if err != nil {
|
||||
+ t.Fatalf("Cannot add parse tree to template: %v", err)
|
||||
+ }
|
||||
+ c.mustExecute(c.root, nil, "1")
|
||||
+}
|
||||
+
|
||||
type testCase struct {
|
||||
t *testing.T
|
||||
root *Template
|
||||
diff --git a/src/text/template/exec.go b/src/text/template/exec.go
|
||||
index ac3e741..7ac5175 100644
|
||||
--- a/src/text/template/exec.go
|
||||
+++ b/src/text/template/exec.go
|
||||
@@ -256,6 +256,7 @@ func (s *state) walk(dot reflect.Value, node parse.Node) {
|
||||
if len(node.Pipe.Decl) == 0 {
|
||||
s.printValue(node, val)
|
||||
}
|
||||
+ case *parse.CommentNode:
|
||||
case *parse.IfNode:
|
||||
s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
|
||||
case *parse.ListNode:
|
||||
diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go
|
||||
index 30371f2..e41373a 100644
|
||||
--- a/src/text/template/parse/lex.go
|
||||
+++ b/src/text/template/parse/lex.go
|
||||
@@ -41,6 +41,7 @@ const (
|
||||
itemBool // boolean constant
|
||||
itemChar // printable ASCII character; grab bag for comma etc.
|
||||
itemCharConstant // character constant
|
||||
+ itemComment // comment text
|
||||
itemComplex // complex constant (1+2i); imaginary is just a number
|
||||
itemAssign // equals ('=') introducing an assignment
|
||||
itemDeclare // colon-equals (':=') introducing a declaration
|
||||
@@ -112,6 +113,7 @@ type lexer struct {
|
||||
leftDelim string // start of action
|
||||
rightDelim string // end of action
|
||||
trimRightDelim string // end of action with trim marker
|
||||
+ emitComment bool // emit itemComment tokens.
|
||||
pos Pos // current position in the input
|
||||
start Pos // start position of this item
|
||||
width Pos // width of last rune read from input
|
||||
@@ -203,7 +205,7 @@ func (l *lexer) drain() {
|
||||
}
|
||||
|
||||
// lex creates a new scanner for the input string.
|
||||
-func lex(name, input, left, right string) *lexer {
|
||||
+func lex(name, input, left, right string, emitComment bool) *lexer {
|
||||
if left == "" {
|
||||
left = leftDelim
|
||||
}
|
||||
@@ -216,6 +218,7 @@ func lex(name, input, left, right string) *lexer {
|
||||
leftDelim: left,
|
||||
rightDelim: right,
|
||||
trimRightDelim: rightTrimMarker + right,
|
||||
+ emitComment: emitComment,
|
||||
items: make(chan item),
|
||||
line: 1,
|
||||
startLine: 1,
|
||||
@@ -323,6 +326,9 @@ func lexComment(l *lexer) stateFn {
|
||||
if !delim {
|
||||
return l.errorf("comment ends before closing delimiter")
|
||||
}
|
||||
+ if l.emitComment {
|
||||
+ l.emit(itemComment)
|
||||
+ }
|
||||
if trimSpace {
|
||||
l.pos += trimMarkerLen
|
||||
}
|
||||
diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go
|
||||
index 563c4fc..f6d5f28 100644
|
||||
--- a/src/text/template/parse/lex_test.go
|
||||
+++ b/src/text/template/parse/lex_test.go
|
||||
@@ -15,6 +15,7 @@ var itemName = map[itemType]string{
|
||||
itemBool: "bool",
|
||||
itemChar: "char",
|
||||
itemCharConstant: "charconst",
|
||||
+ itemComment: "comment",
|
||||
itemComplex: "complex",
|
||||
itemDeclare: ":=",
|
||||
itemEOF: "EOF",
|
||||
@@ -90,6 +91,7 @@ var lexTests = []lexTest{
|
||||
{"text", `now is the time`, []item{mkItem(itemText, "now is the time"), tEOF}},
|
||||
{"text with comment", "hello-{{/* this is a comment */}}-world", []item{
|
||||
mkItem(itemText, "hello-"),
|
||||
+ mkItem(itemComment, "/* this is a comment */"),
|
||||
mkItem(itemText, "-world"),
|
||||
tEOF,
|
||||
}},
|
||||
@@ -311,6 +313,7 @@ var lexTests = []lexTest{
|
||||
}},
|
||||
{"trimming spaces before and after comment", "hello- {{- /* hello */ -}} -world", []item{
|
||||
mkItem(itemText, "hello-"),
|
||||
+ mkItem(itemComment, "/* hello */"),
|
||||
mkItem(itemText, "-world"),
|
||||
tEOF,
|
||||
}},
|
||||
@@ -389,7 +392,7 @@ var lexTests = []lexTest{
|
||||
|
||||
// collect gathers the emitted items into a slice.
|
||||
func collect(t *lexTest, left, right string) (items []item) {
|
||||
- l := lex(t.name, t.input, left, right)
|
||||
+ l := lex(t.name, t.input, left, right, true)
|
||||
for {
|
||||
item := l.nextItem()
|
||||
items = append(items, item)
|
||||
@@ -529,7 +532,7 @@ func TestPos(t *testing.T) {
|
||||
func TestShutdown(t *testing.T) {
|
||||
// We need to duplicate template.Parse here to hold on to the lexer.
|
||||
const text = "erroneous{{define}}{{else}}1234"
|
||||
- lexer := lex("foo", text, "{{", "}}")
|
||||
+ lexer := lex("foo", text, "{{", "}}", false)
|
||||
_, err := New("root").parseLexer(lexer)
|
||||
if err == nil {
|
||||
t.Fatalf("expected error")
|
||||
diff --git a/src/text/template/parse/node.go b/src/text/template/parse/node.go
|
||||
index 1c116ea..a9dad5e 100644
|
||||
--- a/src/text/template/parse/node.go
|
||||
+++ b/src/text/template/parse/node.go
|
||||
@@ -70,6 +70,7 @@ const (
|
||||
NodeTemplate // A template invocation action.
|
||||
NodeVariable // A $ variable.
|
||||
NodeWith // A with action.
|
||||
+ NodeComment // A comment.
|
||||
)
|
||||
|
||||
// Nodes.
|
||||
@@ -149,6 +150,38 @@ func (t *TextNode) Copy() Node {
|
||||
return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)}
|
||||
}
|
||||
|
||||
+// CommentNode holds a comment.
|
||||
+type CommentNode struct {
|
||||
+ NodeType
|
||||
+ Pos
|
||||
+ tr *Tree
|
||||
+ Text string // Comment text.
|
||||
+}
|
||||
+
|
||||
+func (t *Tree) newComment(pos Pos, text string) *CommentNode {
|
||||
+ return &CommentNode{tr: t, NodeType: NodeComment, Pos: pos, Text: text}
|
||||
+}
|
||||
+
|
||||
+func (c *CommentNode) String() string {
|
||||
+ var sb strings.Builder
|
||||
+ c.writeTo(&sb)
|
||||
+ return sb.String()
|
||||
+}
|
||||
+
|
||||
+func (c *CommentNode) writeTo(sb *strings.Builder) {
|
||||
+ sb.WriteString("{{")
|
||||
+ sb.WriteString(c.Text)
|
||||
+ sb.WriteString("}}")
|
||||
+}
|
||||
+
|
||||
+func (c *CommentNode) tree() *Tree {
|
||||
+ return c.tr
|
||||
+}
|
||||
+
|
||||
+func (c *CommentNode) Copy() Node {
|
||||
+ return &CommentNode{tr: c.tr, NodeType: NodeComment, Pos: c.Pos, Text: c.Text}
|
||||
+}
|
||||
+
|
||||
// PipeNode holds a pipeline with optional declaration
|
||||
type PipeNode struct {
|
||||
NodeType
|
||||
diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go
|
||||
index c9b80f4..496d8bf 100644
|
||||
--- a/src/text/template/parse/parse.go
|
||||
+++ b/src/text/template/parse/parse.go
|
||||
@@ -21,6 +21,7 @@ type Tree struct {
|
||||
Name string // name of the template represented by the tree.
|
||||
ParseName string // name of the top-level template during parsing, for error messages.
|
||||
Root *ListNode // top-level root of the tree.
|
||||
+ Mode Mode // parsing mode.
|
||||
text string // text parsed to create the template (or its parent)
|
||||
// Parsing only; cleared after parse.
|
||||
funcs []map[string]interface{}
|
||||
@@ -29,8 +30,16 @@ type Tree struct {
|
||||
peekCount int
|
||||
vars []string // variables defined at the moment.
|
||||
treeSet map[string]*Tree
|
||||
+ mode Mode
|
||||
}
|
||||
|
||||
+// A mode value is a set of flags (or 0). Modes control parser behavior.
|
||||
+type Mode uint
|
||||
+
|
||||
+const (
|
||||
+ ParseComments Mode = 1 << iota // parse comments and add them to AST
|
||||
+)
|
||||
+
|
||||
// Copy returns a copy of the Tree. Any parsing state is discarded.
|
||||
func (t *Tree) Copy() *Tree {
|
||||
if t == nil {
|
||||
@@ -220,7 +229,8 @@ func (t *Tree) stopParse() {
|
||||
func (t *Tree) Parse(text, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {
|
||||
defer t.recover(&err)
|
||||
t.ParseName = t.Name
|
||||
- t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim), treeSet)
|
||||
+ emitComment := t.Mode&ParseComments != 0
|
||||
+ t.startParse(funcs, lex(t.Name, text, leftDelim, rightDelim, emitComment), treeSet)
|
||||
t.text = text
|
||||
t.parse()
|
||||
t.add()
|
||||
@@ -240,12 +250,14 @@ func (t *Tree) add() {
|
||||
}
|
||||
}
|
||||
|
||||
-// IsEmptyTree reports whether this tree (node) is empty of everything but space.
|
||||
+// IsEmptyTree reports whether this tree (node) is empty of everything but space or comments.
|
||||
func IsEmptyTree(n Node) bool {
|
||||
switch n := n.(type) {
|
||||
case nil:
|
||||
return true
|
||||
case *ActionNode:
|
||||
+ case *CommentNode:
|
||||
+ return true
|
||||
case *IfNode:
|
||||
case *ListNode:
|
||||
for _, node := range n.Nodes {
|
||||
@@ -276,6 +288,7 @@ func (t *Tree) parse() {
|
||||
if t.nextNonSpace().typ == itemDefine {
|
||||
newT := New("definition") // name will be updated once we know it.
|
||||
newT.text = t.text
|
||||
+ newT.Mode = t.Mode
|
||||
newT.ParseName = t.ParseName
|
||||
newT.startParse(t.funcs, t.lex, t.treeSet)
|
||||
newT.parseDefinition()
|
||||
@@ -331,13 +344,15 @@ func (t *Tree) itemList() (list *ListNode, next Node) {
|
||||
}
|
||||
|
||||
// textOrAction:
|
||||
-// text | action
|
||||
+// text | comment | action
|
||||
func (t *Tree) textOrAction() Node {
|
||||
switch token := t.nextNonSpace(); token.typ {
|
||||
case itemText:
|
||||
return t.newText(token.pos, token.val)
|
||||
case itemLeftDelim:
|
||||
return t.action()
|
||||
+ case itemComment:
|
||||
+ return t.newComment(token.pos, token.val)
|
||||
default:
|
||||
t.unexpected(token, "input")
|
||||
}
|
||||
@@ -539,6 +554,7 @@ func (t *Tree) blockControl() Node {
|
||||
|
||||
block := New(name) // name will be updated once we know it.
|
||||
block.text = t.text
|
||||
+ block.Mode = t.Mode
|
||||
block.ParseName = t.ParseName
|
||||
block.startParse(t.funcs, t.lex, t.treeSet)
|
||||
var end Node
|
||||
diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go
|
||||
index 4e09a78..d9c13c5 100644
|
||||
--- a/src/text/template/parse/parse_test.go
|
||||
+++ b/src/text/template/parse/parse_test.go
|
||||
@@ -348,6 +348,30 @@ func TestParseCopy(t *testing.T) {
|
||||
testParse(true, t)
|
||||
}
|
||||
|
||||
+func TestParseWithComments(t *testing.T) {
|
||||
+ textFormat = "%q"
|
||||
+ defer func() { textFormat = "%s" }()
|
||||
+ tests := [...]parseTest{
|
||||
+ {"comment", "{{/*\n\n\n*/}}", noError, "{{/*\n\n\n*/}}"},
|
||||
+ {"comment trim left", "x \r\n\t{{- /* hi */}}", noError, `"x"{{/* hi */}}`},
|
||||
+ {"comment trim right", "{{/* hi */ -}}\n\n\ty", noError, `{{/* hi */}}"y"`},
|
||||
+ {"comment trim left and right", "x \r\n\t{{- /* */ -}}\n\n\ty", noError, `"x"{{/* */}}"y"`},
|
||||
+ }
|
||||
+ for _, test := range tests {
|
||||
+ t.Run(test.name, func(t *testing.T) {
|
||||
+ tr := New(test.name)
|
||||
+ tr.Mode = ParseComments
|
||||
+ tmpl, err := tr.Parse(test.input, "", "", make(map[string]*Tree))
|
||||
+ if err != nil {
|
||||
+ t.Errorf("%q: expected error; got none", test.name)
|
||||
+ }
|
||||
+ if result := tmpl.Root.String(); result != test.result {
|
||||
+ t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result)
|
||||
+ }
|
||||
+ })
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
type isEmptyTest struct {
|
||||
name string
|
||||
input string
|
||||
@@ -358,6 +382,7 @@ var isEmptyTests = []isEmptyTest{
|
||||
{"empty", ``, true},
|
||||
{"nonempty", `hello`, false},
|
||||
{"spaces only", " \t\n \t\n", true},
|
||||
+ {"comment only", "{{/* comment */}}", true},
|
||||
{"definition", `{{define "x"}}something{{end}}`, true},
|
||||
{"definitions and space", "{{define `x`}}something{{end}}\n\n{{define `y`}}something{{end}}\n\n", true},
|
||||
{"definitions and text", "{{define `x`}}something{{end}}\nx\n{{define `y`}}something{{end}}\ny\n", false},
|
||||
--
|
||||
2.7.4
|
||||
497
meta/recipes-devtools/go/go-1.14/CVE-2023-24538_4.patch
Normal file
497
meta/recipes-devtools/go/go-1.14/CVE-2023-24538_4.patch
Normal file
@@ -0,0 +1,497 @@
|
||||
From 760d88497091fb5d6d231a18e6f4e06ecb9af9b2 Mon Sep 17 00:00:00 2001
|
||||
From: Russ Cox <rsc@golang.org>
|
||||
Date: Thu, 10 Sep 2020 18:53:26 -0400
|
||||
Subject: [PATCH 4/6] text/template: allow newlines inside action delimiters
|
||||
|
||||
This allows multiline constructs like:
|
||||
|
||||
{{"hello" |
|
||||
printf}}
|
||||
|
||||
Now that unclosed actions can span multiple lines,
|
||||
track and report the start of the action when reporting errors.
|
||||
|
||||
Also clean up a few "unexpected <error message>" to be just "<error message>".
|
||||
|
||||
Fixes #29770.
|
||||
|
||||
Change-Id: I54c6c016029a8328b7902a4b6d85eab713ec3285
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/254257
|
||||
Trust: Russ Cox <rsc@golang.org>
|
||||
Run-TryBot: Russ Cox <rsc@golang.org>
|
||||
TryBot-Result: Go Bot <gobot@golang.org>
|
||||
Reviewed-by: Rob Pike <r@golang.org>
|
||||
|
||||
Dependency Patch #4
|
||||
|
||||
Upstream-Status: Backport from https://github.com/golang/go/commit/9384d34c58099657bb1b133beaf3ff37ada9b017
|
||||
CVE: CVE-2023-24538
|
||||
Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
|
||||
---
|
||||
src/text/template/doc.go | 21 ++++-----
|
||||
src/text/template/exec_test.go | 2 +-
|
||||
src/text/template/parse/lex.go | 84 +++++++++++++++++------------------
|
||||
src/text/template/parse/lex_test.go | 2 +-
|
||||
src/text/template/parse/parse.go | 59 +++++++++++++-----------
|
||||
src/text/template/parse/parse_test.go | 36 ++++++++++++---
|
||||
6 files changed, 117 insertions(+), 87 deletions(-)
|
||||
|
||||
diff --git a/src/text/template/doc.go b/src/text/template/doc.go
|
||||
index 4b0efd2..7b30294 100644
|
||||
--- a/src/text/template/doc.go
|
||||
+++ b/src/text/template/doc.go
|
||||
@@ -40,16 +40,17 @@ More intricate examples appear below.
|
||||
Text and spaces
|
||||
|
||||
By default, all text between actions is copied verbatim when the template is
|
||||
-executed. For example, the string " items are made of " in the example above appears
|
||||
-on standard output when the program is run.
|
||||
-
|
||||
-However, to aid in formatting template source code, if an action's left delimiter
|
||||
-(by default "{{") is followed immediately by a minus sign and ASCII space character
|
||||
-("{{- "), all trailing white space is trimmed from the immediately preceding text.
|
||||
-Similarly, if the right delimiter ("}}") is preceded by a space and minus sign
|
||||
-(" -}}"), all leading white space is trimmed from the immediately following text.
|
||||
-In these trim markers, the ASCII space must be present; "{{-3}}" parses as an
|
||||
-action containing the number -3.
|
||||
+executed. For example, the string " items are made of " in the example above
|
||||
+appears on standard output when the program is run.
|
||||
+
|
||||
+However, to aid in formatting template source code, if an action's left
|
||||
+delimiter (by default "{{") is followed immediately by a minus sign and white
|
||||
+space, all trailing white space is trimmed from the immediately preceding text.
|
||||
+Similarly, if the right delimiter ("}}") is preceded by white space and a minus
|
||||
+sign, all leading white space is trimmed from the immediately following text.
|
||||
+In these trim markers, the white space must be present:
|
||||
+"{{- 3}}" is like "{{3}}" but trims the immediately preceding text, while
|
||||
+"{{-3}}" parses as an action containing the number -3.
|
||||
|
||||
For instance, when executing the template whose source is
|
||||
|
||||
diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go
|
||||
index b8a809e..3309b33 100644
|
||||
--- a/src/text/template/exec_test.go
|
||||
+++ b/src/text/template/exec_test.go
|
||||
@@ -1295,7 +1295,7 @@ func TestUnterminatedStringError(t *testing.T) {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
str := err.Error()
|
||||
- if !strings.Contains(str, "X:3: unexpected unterminated raw quoted string") {
|
||||
+ if !strings.Contains(str, "X:3: unterminated raw quoted string") {
|
||||
t.Fatalf("unexpected error: %s", str)
|
||||
}
|
||||
}
|
||||
diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go
|
||||
index e41373a..6784071 100644
|
||||
--- a/src/text/template/parse/lex.go
|
||||
+++ b/src/text/template/parse/lex.go
|
||||
@@ -92,15 +92,14 @@ const eof = -1
|
||||
// If the action begins "{{- " rather than "{{", then all space/tab/newlines
|
||||
// preceding the action are trimmed; conversely if it ends " -}}" the
|
||||
// leading spaces are trimmed. This is done entirely in the lexer; the
|
||||
-// parser never sees it happen. We require an ASCII space to be
|
||||
-// present to avoid ambiguity with things like "{{-3}}". It reads
|
||||
+// parser never sees it happen. We require an ASCII space (' ', \t, \r, \n)
|
||||
+// to be present to avoid ambiguity with things like "{{-3}}". It reads
|
||||
// better with the space present anyway. For simplicity, only ASCII
|
||||
-// space does the job.
|
||||
+// does the job.
|
||||
const (
|
||||
- spaceChars = " \t\r\n" // These are the space characters defined by Go itself.
|
||||
- leftTrimMarker = "- " // Attached to left delimiter, trims trailing spaces from preceding text.
|
||||
- rightTrimMarker = " -" // Attached to right delimiter, trims leading spaces from following text.
|
||||
- trimMarkerLen = Pos(len(leftTrimMarker))
|
||||
+ spaceChars = " \t\r\n" // These are the space characters defined by Go itself.
|
||||
+ trimMarker = '-' // Attached to left/right delimiter, trims trailing spaces from preceding/following text.
|
||||
+ trimMarkerLen = Pos(1 + 1) // marker plus space before or after
|
||||
)
|
||||
|
||||
// stateFn represents the state of the scanner as a function that returns the next state.
|
||||
@@ -108,19 +107,18 @@ type stateFn func(*lexer) stateFn
|
||||
|
||||
// lexer holds the state of the scanner.
|
||||
type lexer struct {
|
||||
- name string // the name of the input; used only for error reports
|
||||
- input string // the string being scanned
|
||||
- leftDelim string // start of action
|
||||
- rightDelim string // end of action
|
||||
- trimRightDelim string // end of action with trim marker
|
||||
- emitComment bool // emit itemComment tokens.
|
||||
- pos Pos // current position in the input
|
||||
- start Pos // start position of this item
|
||||
- width Pos // width of last rune read from input
|
||||
- items chan item // channel of scanned items
|
||||
- parenDepth int // nesting depth of ( ) exprs
|
||||
- line int // 1+number of newlines seen
|
||||
- startLine int // start line of this item
|
||||
+ name string // the name of the input; used only for error reports
|
||||
+ input string // the string being scanned
|
||||
+ leftDelim string // start of action
|
||||
+ rightDelim string // end of action
|
||||
+ emitComment bool // emit itemComment tokens.
|
||||
+ pos Pos // current position in the input
|
||||
+ start Pos // start position of this item
|
||||
+ width Pos // width of last rune read from input
|
||||
+ items chan item // channel of scanned items
|
||||
+ parenDepth int // nesting depth of ( ) exprs
|
||||
+ line int // 1+number of newlines seen
|
||||
+ startLine int // start line of this item
|
||||
}
|
||||
|
||||
// next returns the next rune in the input.
|
||||
@@ -213,15 +211,14 @@ func lex(name, input, left, right string, emitComment bool) *lexer {
|
||||
right = rightDelim
|
||||
}
|
||||
l := &lexer{
|
||||
- name: name,
|
||||
- input: input,
|
||||
- leftDelim: left,
|
||||
- rightDelim: right,
|
||||
- trimRightDelim: rightTrimMarker + right,
|
||||
- emitComment: emitComment,
|
||||
- items: make(chan item),
|
||||
- line: 1,
|
||||
- startLine: 1,
|
||||
+ name: name,
|
||||
+ input: input,
|
||||
+ leftDelim: left,
|
||||
+ rightDelim: right,
|
||||
+ emitComment: emitComment,
|
||||
+ items: make(chan item),
|
||||
+ line: 1,
|
||||
+ startLine: 1,
|
||||
}
|
||||
go l.run()
|
||||
return l
|
||||
@@ -251,7 +248,7 @@ func lexText(l *lexer) stateFn {
|
||||
ldn := Pos(len(l.leftDelim))
|
||||
l.pos += Pos(x)
|
||||
trimLength := Pos(0)
|
||||
- if strings.HasPrefix(l.input[l.pos+ldn:], leftTrimMarker) {
|
||||
+ if hasLeftTrimMarker(l.input[l.pos+ldn:]) {
|
||||
trimLength = rightTrimLength(l.input[l.start:l.pos])
|
||||
}
|
||||
l.pos -= trimLength
|
||||
@@ -280,7 +277,7 @@ func rightTrimLength(s string) Pos {
|
||||
|
||||
// atRightDelim reports whether the lexer is at a right delimiter, possibly preceded by a trim marker.
|
||||
func (l *lexer) atRightDelim() (delim, trimSpaces bool) {
|
||||
- if strings.HasPrefix(l.input[l.pos:], l.trimRightDelim) { // With trim marker.
|
||||
+ if hasRightTrimMarker(l.input[l.pos:]) && strings.HasPrefix(l.input[l.pos+trimMarkerLen:], l.rightDelim) { // With trim marker.
|
||||
return true, true
|
||||
}
|
||||
if strings.HasPrefix(l.input[l.pos:], l.rightDelim) { // Without trim marker.
|
||||
@@ -297,7 +294,7 @@ func leftTrimLength(s string) Pos {
|
||||
// lexLeftDelim scans the left delimiter, which is known to be present, possibly with a trim marker.
|
||||
func lexLeftDelim(l *lexer) stateFn {
|
||||
l.pos += Pos(len(l.leftDelim))
|
||||
- trimSpace := strings.HasPrefix(l.input[l.pos:], leftTrimMarker)
|
||||
+ trimSpace := hasLeftTrimMarker(l.input[l.pos:])
|
||||
afterMarker := Pos(0)
|
||||
if trimSpace {
|
||||
afterMarker = trimMarkerLen
|
||||
@@ -342,7 +339,7 @@ func lexComment(l *lexer) stateFn {
|
||||
|
||||
// lexRightDelim scans the right delimiter, which is known to be present, possibly with a trim marker.
|
||||
func lexRightDelim(l *lexer) stateFn {
|
||||
- trimSpace := strings.HasPrefix(l.input[l.pos:], rightTrimMarker)
|
||||
+ trimSpace := hasRightTrimMarker(l.input[l.pos:])
|
||||
if trimSpace {
|
||||
l.pos += trimMarkerLen
|
||||
l.ignore()
|
||||
@@ -369,7 +366,7 @@ func lexInsideAction(l *lexer) stateFn {
|
||||
return l.errorf("unclosed left paren")
|
||||
}
|
||||
switch r := l.next(); {
|
||||
- case r == eof || isEndOfLine(r):
|
||||
+ case r == eof:
|
||||
return l.errorf("unclosed action")
|
||||
case isSpace(r):
|
||||
l.backup() // Put space back in case we have " -}}".
|
||||
@@ -439,7 +436,7 @@ func lexSpace(l *lexer) stateFn {
|
||||
}
|
||||
// Be careful about a trim-marked closing delimiter, which has a minus
|
||||
// after a space. We know there is a space, so check for the '-' that might follow.
|
||||
- if strings.HasPrefix(l.input[l.pos-1:], l.trimRightDelim) {
|
||||
+ if hasRightTrimMarker(l.input[l.pos-1:]) && strings.HasPrefix(l.input[l.pos-1+trimMarkerLen:], l.rightDelim) {
|
||||
l.backup() // Before the space.
|
||||
if numSpaces == 1 {
|
||||
return lexRightDelim // On the delim, so go right to that.
|
||||
@@ -526,7 +523,7 @@ func lexFieldOrVariable(l *lexer, typ itemType) stateFn {
|
||||
// day to implement arithmetic.
|
||||
func (l *lexer) atTerminator() bool {
|
||||
r := l.peek()
|
||||
- if isSpace(r) || isEndOfLine(r) {
|
||||
+ if isSpace(r) {
|
||||
return true
|
||||
}
|
||||
switch r {
|
||||
@@ -657,15 +654,18 @@ Loop:
|
||||
|
||||
// isSpace reports whether r is a space character.
|
||||
func isSpace(r rune) bool {
|
||||
- return r == ' ' || r == '\t'
|
||||
-}
|
||||
-
|
||||
-// isEndOfLine reports whether r is an end-of-line character.
|
||||
-func isEndOfLine(r rune) bool {
|
||||
- return r == '\r' || r == '\n'
|
||||
+ return r == ' ' || r == '\t' || r == '\r' || r == '\n'
|
||||
}
|
||||
|
||||
// isAlphaNumeric reports whether r is an alphabetic, digit, or underscore.
|
||||
func isAlphaNumeric(r rune) bool {
|
||||
return r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r)
|
||||
}
|
||||
+
|
||||
+func hasLeftTrimMarker(s string) bool {
|
||||
+ return len(s) >= 2 && s[0] == trimMarker && isSpace(rune(s[1]))
|
||||
+}
|
||||
+
|
||||
+func hasRightTrimMarker(s string) bool {
|
||||
+ return len(s) >= 2 && isSpace(rune(s[0])) && s[1] == trimMarker
|
||||
+}
|
||||
diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go
|
||||
index f6d5f28..6510eed 100644
|
||||
--- a/src/text/template/parse/lex_test.go
|
||||
+++ b/src/text/template/parse/lex_test.go
|
||||
@@ -323,7 +323,7 @@ var lexTests = []lexTest{
|
||||
tLeft,
|
||||
mkItem(itemError, "unrecognized character in action: U+0001"),
|
||||
}},
|
||||
- {"unclosed action", "{{\n}}", []item{
|
||||
+ {"unclosed action", "{{", []item{
|
||||
tLeft,
|
||||
mkItem(itemError, "unclosed action"),
|
||||
}},
|
||||
diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go
|
||||
index 496d8bf..5e6e512 100644
|
||||
--- a/src/text/template/parse/parse.go
|
||||
+++ b/src/text/template/parse/parse.go
|
||||
@@ -24,13 +24,14 @@ type Tree struct {
|
||||
Mode Mode // parsing mode.
|
||||
text string // text parsed to create the template (or its parent)
|
||||
// Parsing only; cleared after parse.
|
||||
- funcs []map[string]interface{}
|
||||
- lex *lexer
|
||||
- token [3]item // three-token lookahead for parser.
|
||||
- peekCount int
|
||||
- vars []string // variables defined at the moment.
|
||||
- treeSet map[string]*Tree
|
||||
- mode Mode
|
||||
+ funcs []map[string]interface{}
|
||||
+ lex *lexer
|
||||
+ token [3]item // three-token lookahead for parser.
|
||||
+ peekCount int
|
||||
+ vars []string // variables defined at the moment.
|
||||
+ treeSet map[string]*Tree
|
||||
+ actionLine int // line of left delim starting action
|
||||
+ mode Mode
|
||||
}
|
||||
|
||||
// A mode value is a set of flags (or 0). Modes control parser behavior.
|
||||
@@ -187,6 +188,16 @@ func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {
|
||||
|
||||
// unexpected complains about the token and terminates processing.
|
||||
func (t *Tree) unexpected(token item, context string) {
|
||||
+ if token.typ == itemError {
|
||||
+ extra := ""
|
||||
+ if t.actionLine != 0 && t.actionLine != token.line {
|
||||
+ extra = fmt.Sprintf(" in action started at %s:%d", t.ParseName, t.actionLine)
|
||||
+ if strings.HasSuffix(token.val, " action") {
|
||||
+ extra = extra[len(" in action"):] // avoid "action in action"
|
||||
+ }
|
||||
+ }
|
||||
+ t.errorf("%s%s", token, extra)
|
||||
+ }
|
||||
t.errorf("unexpected %s in %s", token, context)
|
||||
}
|
||||
|
||||
@@ -350,6 +361,8 @@ func (t *Tree) textOrAction() Node {
|
||||
case itemText:
|
||||
return t.newText(token.pos, token.val)
|
||||
case itemLeftDelim:
|
||||
+ t.actionLine = token.line
|
||||
+ defer t.clearActionLine()
|
||||
return t.action()
|
||||
case itemComment:
|
||||
return t.newComment(token.pos, token.val)
|
||||
@@ -359,6 +372,10 @@ func (t *Tree) textOrAction() Node {
|
||||
return nil
|
||||
}
|
||||
|
||||
+func (t *Tree) clearActionLine() {
|
||||
+ t.actionLine = 0
|
||||
+}
|
||||
+
|
||||
// Action:
|
||||
// control
|
||||
// command ("|" command)*
|
||||
@@ -384,12 +401,12 @@ func (t *Tree) action() (n Node) {
|
||||
t.backup()
|
||||
token := t.peek()
|
||||
// Do not pop variables; they persist until "end".
|
||||
- return t.newAction(token.pos, token.line, t.pipeline("command"))
|
||||
+ return t.newAction(token.pos, token.line, t.pipeline("command", itemRightDelim))
|
||||
}
|
||||
|
||||
// Pipeline:
|
||||
// declarations? command ('|' command)*
|
||||
-func (t *Tree) pipeline(context string) (pipe *PipeNode) {
|
||||
+func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) {
|
||||
token := t.peekNonSpace()
|
||||
pipe = t.newPipeline(token.pos, token.line, nil)
|
||||
// Are there declarations or assignments?
|
||||
@@ -430,12 +447,9 @@ decls:
|
||||
}
|
||||
for {
|
||||
switch token := t.nextNonSpace(); token.typ {
|
||||
- case itemRightDelim, itemRightParen:
|
||||
+ case end:
|
||||
// At this point, the pipeline is complete
|
||||
t.checkPipeline(pipe, context)
|
||||
- if token.typ == itemRightParen {
|
||||
- t.backup()
|
||||
- }
|
||||
return
|
||||
case itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,
|
||||
itemNumber, itemNil, itemRawString, itemString, itemVariable, itemLeftParen:
|
||||
@@ -464,7 +478,7 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
|
||||
|
||||
func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
|
||||
defer t.popVars(len(t.vars))
|
||||
- pipe = t.pipeline(context)
|
||||
+ pipe = t.pipeline(context, itemRightDelim)
|
||||
var next Node
|
||||
list, next = t.itemList()
|
||||
switch next.Type() {
|
||||
@@ -550,7 +564,7 @@ func (t *Tree) blockControl() Node {
|
||||
|
||||
token := t.nextNonSpace()
|
||||
name := t.parseTemplateName(token, context)
|
||||
- pipe := t.pipeline(context)
|
||||
+ pipe := t.pipeline(context, itemRightDelim)
|
||||
|
||||
block := New(name) // name will be updated once we know it.
|
||||
block.text = t.text
|
||||
@@ -580,7 +594,7 @@ func (t *Tree) templateControl() Node {
|
||||
if t.nextNonSpace().typ != itemRightDelim {
|
||||
t.backup()
|
||||
// Do not pop variables; they persist until "end".
|
||||
- pipe = t.pipeline(context)
|
||||
+ pipe = t.pipeline(context, itemRightDelim)
|
||||
}
|
||||
return t.newTemplate(token.pos, token.line, name, pipe)
|
||||
}
|
||||
@@ -614,13 +628,12 @@ func (t *Tree) command() *CommandNode {
|
||||
switch token := t.next(); token.typ {
|
||||
case itemSpace:
|
||||
continue
|
||||
- case itemError:
|
||||
- t.errorf("%s", token.val)
|
||||
case itemRightDelim, itemRightParen:
|
||||
t.backup()
|
||||
case itemPipe:
|
||||
+ // nothing here; break loop below
|
||||
default:
|
||||
- t.errorf("unexpected %s in operand", token)
|
||||
+ t.unexpected(token, "operand")
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -675,8 +688,6 @@ func (t *Tree) operand() Node {
|
||||
// A nil return means the next item is not a term.
|
||||
func (t *Tree) term() Node {
|
||||
switch token := t.nextNonSpace(); token.typ {
|
||||
- case itemError:
|
||||
- t.errorf("%s", token.val)
|
||||
case itemIdentifier:
|
||||
if !t.hasFunction(token.val) {
|
||||
t.errorf("function %q not defined", token.val)
|
||||
@@ -699,11 +710,7 @@ func (t *Tree) term() Node {
|
||||
}
|
||||
return number
|
||||
case itemLeftParen:
|
||||
- pipe := t.pipeline("parenthesized pipeline")
|
||||
- if token := t.next(); token.typ != itemRightParen {
|
||||
- t.errorf("unclosed right paren: unexpected %s", token)
|
||||
- }
|
||||
- return pipe
|
||||
+ return t.pipeline("parenthesized pipeline", itemRightParen)
|
||||
case itemString, itemRawString:
|
||||
s, err := strconv.Unquote(token.val)
|
||||
if err != nil {
|
||||
diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go
|
||||
index d9c13c5..220f984 100644
|
||||
--- a/src/text/template/parse/parse_test.go
|
||||
+++ b/src/text/template/parse/parse_test.go
|
||||
@@ -250,6 +250,13 @@ var parseTests = []parseTest{
|
||||
{"comment trim left and right", "x \r\n\t{{- /* */ -}}\n\n\ty", noError, `"x""y"`},
|
||||
{"block definition", `{{block "foo" .}}hello{{end}}`, noError,
|
||||
`{{template "foo" .}}`},
|
||||
+
|
||||
+ {"newline in assignment", "{{ $x \n := \n 1 \n }}", noError, "{{$x := 1}}"},
|
||||
+ {"newline in empty action", "{{\n}}", hasError, "{{\n}}"},
|
||||
+ {"newline in pipeline", "{{\n\"x\"\n|\nprintf\n}}", noError, `{{"x" | printf}}`},
|
||||
+ {"newline in comment", "{{/*\nhello\n*/}}", noError, ""},
|
||||
+ {"newline in comment", "{{-\n/*\nhello\n*/\n-}}", noError, ""},
|
||||
+
|
||||
// Errors.
|
||||
{"unclosed action", "hello{{range", hasError, ""},
|
||||
{"unmatched end", "{{end}}", hasError, ""},
|
||||
@@ -426,23 +433,38 @@ var errorTests = []parseTest{
|
||||
// Check line numbers are accurate.
|
||||
{"unclosed1",
|
||||
"line1\n{{",
|
||||
- hasError, `unclosed1:2: unexpected unclosed action in command`},
|
||||
+ hasError, `unclosed1:2: unclosed action`},
|
||||
{"unclosed2",
|
||||
"line1\n{{define `x`}}line2\n{{",
|
||||
- hasError, `unclosed2:3: unexpected unclosed action in command`},
|
||||
+ hasError, `unclosed2:3: unclosed action`},
|
||||
+ {"unclosed3",
|
||||
+ "line1\n{{\"x\"\n\"y\"\n",
|
||||
+ hasError, `unclosed3:4: unclosed action started at unclosed3:2`},
|
||||
+ {"unclosed4",
|
||||
+ "{{\n\n\n\n\n",
|
||||
+ hasError, `unclosed4:6: unclosed action started at unclosed4:1`},
|
||||
+ {"var1",
|
||||
+ "line1\n{{\nx\n}}",
|
||||
+ hasError, `var1:3: function "x" not defined`},
|
||||
// Specific errors.
|
||||
{"function",
|
||||
"{{foo}}",
|
||||
hasError, `function "foo" not defined`},
|
||||
- {"comment",
|
||||
+ {"comment1",
|
||||
"{{/*}}",
|
||||
- hasError, `unclosed comment`},
|
||||
+ hasError, `comment1:1: unclosed comment`},
|
||||
+ {"comment2",
|
||||
+ "{{/*\nhello\n}}",
|
||||
+ hasError, `comment2:1: unclosed comment`},
|
||||
{"lparen",
|
||||
"{{.X (1 2 3}}",
|
||||
hasError, `unclosed left paren`},
|
||||
{"rparen",
|
||||
- "{{.X 1 2 3)}}",
|
||||
- hasError, `unexpected ")"`},
|
||||
+ "{{.X 1 2 3 ) }}",
|
||||
+ hasError, `unexpected ")" in command`},
|
||||
+ {"rparen2",
|
||||
+ "{{(.X 1 2 3",
|
||||
+ hasError, `unclosed action`},
|
||||
{"space",
|
||||
"{{`x`3}}",
|
||||
hasError, `in operand`},
|
||||
@@ -488,7 +510,7 @@ var errorTests = []parseTest{
|
||||
hasError, `missing value for parenthesized pipeline`},
|
||||
{"multilinerawstring",
|
||||
"{{ $v := `\n` }} {{",
|
||||
- hasError, `multilinerawstring:2: unexpected unclosed action`},
|
||||
+ hasError, `multilinerawstring:2: unclosed action`},
|
||||
{"rangeundefvar",
|
||||
"{{range $k}}{{end}}",
|
||||
hasError, `undefined variable`},
|
||||
--
|
||||
2.7.4
|
||||
585
meta/recipes-devtools/go/go-1.14/CVE-2023-24538_5.patch
Normal file
585
meta/recipes-devtools/go/go-1.14/CVE-2023-24538_5.patch
Normal file
@@ -0,0 +1,585 @@
|
||||
From e0e6bca6ddc0e6d9fa3a5b644af9b446924fbf83 Mon Sep 17 00:00:00 2001
|
||||
From: Russ Cox <rsc@golang.org>
|
||||
Date: Thu, 20 May 2021 12:46:33 -0400
|
||||
Subject: [PATCH 5/6] html/template, text/template: implement break and
|
||||
continue for range loops
|
||||
|
||||
Break and continue for range loops was accepted as a proposal in June 2017.
|
||||
It was implemented in CL 66410 (Oct 2017)
|
||||
but then rolled back in CL 92155 (Feb 2018)
|
||||
because html/template changes had not been implemented.
|
||||
|
||||
This CL reimplements break and continue in text/template
|
||||
and then adds support for them in html/template as well.
|
||||
|
||||
Fixes #20531.
|
||||
|
||||
Change-Id: I05330482a976f1c078b4b49c2287bd9031bb7616
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/321491
|
||||
Trust: Russ Cox <rsc@golang.org>
|
||||
Run-TryBot: Russ Cox <rsc@golang.org>
|
||||
TryBot-Result: Go Bot <gobot@golang.org>
|
||||
Reviewed-by: Rob Pike <r@golang.org>
|
||||
|
||||
Dependency Patch #5
|
||||
|
||||
Upstream-Status: Backport from https://github.com/golang/go/commit/d0dd26a88c019d54f22463daae81e785f5867565
|
||||
CVE: CVE-2023-24538
|
||||
Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
|
||||
---
|
||||
src/html/template/context.go | 4 ++
|
||||
src/html/template/escape.go | 71 ++++++++++++++++++++++++++++++++++-
|
||||
src/html/template/escape_test.go | 24 ++++++++++++
|
||||
src/text/template/doc.go | 8 ++++
|
||||
src/text/template/exec.go | 24 +++++++++++-
|
||||
src/text/template/exec_test.go | 2 +
|
||||
src/text/template/parse/lex.go | 13 ++++++-
|
||||
src/text/template/parse/lex_test.go | 2 +
|
||||
src/text/template/parse/node.go | 36 ++++++++++++++++++
|
||||
src/text/template/parse/parse.go | 42 ++++++++++++++++++++-
|
||||
src/text/template/parse/parse_test.go | 8 ++++
|
||||
11 files changed, 230 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/html/template/context.go b/src/html/template/context.go
|
||||
index f7d4849..aaa7d08 100644
|
||||
--- a/src/html/template/context.go
|
||||
+++ b/src/html/template/context.go
|
||||
@@ -6,6 +6,7 @@ package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
+ "text/template/parse"
|
||||
)
|
||||
|
||||
// context describes the state an HTML parser must be in when it reaches the
|
||||
@@ -22,6 +23,7 @@ type context struct {
|
||||
jsCtx jsCtx
|
||||
attr attr
|
||||
element element
|
||||
+ n parse.Node // for range break/continue
|
||||
err *Error
|
||||
}
|
||||
|
||||
@@ -141,6 +143,8 @@ const (
|
||||
// stateError is an infectious error state outside any valid
|
||||
// HTML/CSS/JS construct.
|
||||
stateError
|
||||
+ // stateDead marks unreachable code after a {{break}} or {{continue}}.
|
||||
+ stateDead
|
||||
)
|
||||
|
||||
// isComment is true for any state that contains content meant for template
|
||||
diff --git a/src/html/template/escape.go b/src/html/template/escape.go
|
||||
index 8739735..6dea79c 100644
|
||||
--- a/src/html/template/escape.go
|
||||
+++ b/src/html/template/escape.go
|
||||
@@ -97,6 +97,15 @@ type escaper struct {
|
||||
actionNodeEdits map[*parse.ActionNode][]string
|
||||
templateNodeEdits map[*parse.TemplateNode]string
|
||||
textNodeEdits map[*parse.TextNode][]byte
|
||||
+ // rangeContext holds context about the current range loop.
|
||||
+ rangeContext *rangeContext
|
||||
+}
|
||||
+
|
||||
+// rangeContext holds information about the current range loop.
|
||||
+type rangeContext struct {
|
||||
+ outer *rangeContext // outer loop
|
||||
+ breaks []context // context at each break action
|
||||
+ continues []context // context at each continue action
|
||||
}
|
||||
|
||||
// makeEscaper creates a blank escaper for the given set.
|
||||
@@ -109,6 +118,7 @@ func makeEscaper(n *nameSpace) escaper {
|
||||
map[*parse.ActionNode][]string{},
|
||||
map[*parse.TemplateNode]string{},
|
||||
map[*parse.TextNode][]byte{},
|
||||
+ nil,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,8 +134,16 @@ func (e *escaper) escape(c context, n parse.Node) context {
|
||||
switch n := n.(type) {
|
||||
case *parse.ActionNode:
|
||||
return e.escapeAction(c, n)
|
||||
+ case *parse.BreakNode:
|
||||
+ c.n = n
|
||||
+ e.rangeContext.breaks = append(e.rangeContext.breaks, c)
|
||||
+ return context{state: stateDead}
|
||||
case *parse.CommentNode:
|
||||
return c
|
||||
+ case *parse.ContinueNode:
|
||||
+ c.n = n
|
||||
+ e.rangeContext.continues = append(e.rangeContext.breaks, c)
|
||||
+ return context{state: stateDead}
|
||||
case *parse.IfNode:
|
||||
return e.escapeBranch(c, &n.BranchNode, "if")
|
||||
case *parse.ListNode:
|
||||
@@ -427,6 +445,12 @@ func join(a, b context, node parse.Node, nodeName string) context {
|
||||
if b.state == stateError {
|
||||
return b
|
||||
}
|
||||
+ if a.state == stateDead {
|
||||
+ return b
|
||||
+ }
|
||||
+ if b.state == stateDead {
|
||||
+ return a
|
||||
+ }
|
||||
if a.eq(b) {
|
||||
return a
|
||||
}
|
||||
@@ -466,14 +490,27 @@ func join(a, b context, node parse.Node, nodeName string) context {
|
||||
|
||||
// escapeBranch escapes a branch template node: "if", "range" and "with".
|
||||
func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string) context {
|
||||
+ if nodeName == "range" {
|
||||
+ e.rangeContext = &rangeContext{outer: e.rangeContext}
|
||||
+ }
|
||||
c0 := e.escapeList(c, n.List)
|
||||
- if nodeName == "range" && c0.state != stateError {
|
||||
+ if nodeName == "range" {
|
||||
+ if c0.state != stateError {
|
||||
+ c0 = joinRange(c0, e.rangeContext)
|
||||
+ }
|
||||
+ e.rangeContext = e.rangeContext.outer
|
||||
+ if c0.state == stateError {
|
||||
+ return c0
|
||||
+ }
|
||||
+
|
||||
// The "true" branch of a "range" node can execute multiple times.
|
||||
// We check that executing n.List once results in the same context
|
||||
// as executing n.List twice.
|
||||
+ e.rangeContext = &rangeContext{outer: e.rangeContext}
|
||||
c1, _ := e.escapeListConditionally(c0, n.List, nil)
|
||||
c0 = join(c0, c1, n, nodeName)
|
||||
if c0.state == stateError {
|
||||
+ e.rangeContext = e.rangeContext.outer
|
||||
// Make clear that this is a problem on loop re-entry
|
||||
// since developers tend to overlook that branch when
|
||||
// debugging templates.
|
||||
@@ -481,11 +518,39 @@ func (e *escaper) escapeBranch(c context, n *parse.BranchNode, nodeName string)
|
||||
c0.err.Description = "on range loop re-entry: " + c0.err.Description
|
||||
return c0
|
||||
}
|
||||
+ c0 = joinRange(c0, e.rangeContext)
|
||||
+ e.rangeContext = e.rangeContext.outer
|
||||
+ if c0.state == stateError {
|
||||
+ return c0
|
||||
+ }
|
||||
}
|
||||
c1 := e.escapeList(c, n.ElseList)
|
||||
return join(c0, c1, n, nodeName)
|
||||
}
|
||||
|
||||
+func joinRange(c0 context, rc *rangeContext) context {
|
||||
+ // Merge contexts at break and continue statements into overall body context.
|
||||
+ // In theory we could treat breaks differently from continues, but for now it is
|
||||
+ // enough to treat them both as going back to the start of the loop (which may then stop).
|
||||
+ for _, c := range rc.breaks {
|
||||
+ c0 = join(c0, c, c.n, "range")
|
||||
+ if c0.state == stateError {
|
||||
+ c0.err.Line = c.n.(*parse.BreakNode).Line
|
||||
+ c0.err.Description = "at range loop break: " + c0.err.Description
|
||||
+ return c0
|
||||
+ }
|
||||
+ }
|
||||
+ for _, c := range rc.continues {
|
||||
+ c0 = join(c0, c, c.n, "range")
|
||||
+ if c0.state == stateError {
|
||||
+ c0.err.Line = c.n.(*parse.ContinueNode).Line
|
||||
+ c0.err.Description = "at range loop continue: " + c0.err.Description
|
||||
+ return c0
|
||||
+ }
|
||||
+ }
|
||||
+ return c0
|
||||
+}
|
||||
+
|
||||
// escapeList escapes a list template node.
|
||||
func (e *escaper) escapeList(c context, n *parse.ListNode) context {
|
||||
if n == nil {
|
||||
@@ -493,6 +558,9 @@ func (e *escaper) escapeList(c context, n *parse.ListNode) context {
|
||||
}
|
||||
for _, m := range n.Nodes {
|
||||
c = e.escape(c, m)
|
||||
+ if c.state == stateDead {
|
||||
+ break
|
||||
+ }
|
||||
}
|
||||
return c
|
||||
}
|
||||
@@ -503,6 +571,7 @@ func (e *escaper) escapeList(c context, n *parse.ListNode) context {
|
||||
// which is the same as whether e was updated.
|
||||
func (e *escaper) escapeListConditionally(c context, n *parse.ListNode, filter func(*escaper, context) bool) (context, bool) {
|
||||
e1 := makeEscaper(e.ns)
|
||||
+ e1.rangeContext = e.rangeContext
|
||||
// Make type inferences available to f.
|
||||
for k, v := range e.output {
|
||||
e1.output[k] = v
|
||||
diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
|
||||
index c709660..fa2b84a 100644
|
||||
--- a/src/html/template/escape_test.go
|
||||
+++ b/src/html/template/escape_test.go
|
||||
@@ -920,6 +920,22 @@ func TestErrors(t *testing.T) {
|
||||
"<a href='/foo?{{range .Items}}&{{.K}}={{.V}}{{end}}'>",
|
||||
"",
|
||||
},
|
||||
+ {
|
||||
+ "{{range .Items}}<a{{if .X}}{{end}}>{{end}}",
|
||||
+ "",
|
||||
+ },
|
||||
+ {
|
||||
+ "{{range .Items}}<a{{if .X}}{{end}}>{{continue}}{{end}}",
|
||||
+ "",
|
||||
+ },
|
||||
+ {
|
||||
+ "{{range .Items}}<a{{if .X}}{{end}}>{{break}}{{end}}",
|
||||
+ "",
|
||||
+ },
|
||||
+ {
|
||||
+ "{{range .Items}}<a{{if .X}}{{end}}>{{if .X}}{{break}}{{end}}{{end}}",
|
||||
+ "",
|
||||
+ },
|
||||
// Error cases.
|
||||
{
|
||||
"{{if .Cond}}<a{{end}}",
|
||||
@@ -956,6 +972,14 @@ func TestErrors(t *testing.T) {
|
||||
"z:2:8: on range loop re-entry: {{range}} branches",
|
||||
},
|
||||
{
|
||||
+ "{{range .Items}}<a{{if .X}}{{break}}{{end}}>{{end}}",
|
||||
+ "z:1:29: at range loop break: {{range}} branches end in different contexts",
|
||||
+ },
|
||||
+ {
|
||||
+ "{{range .Items}}<a{{if .X}}{{continue}}{{end}}>{{end}}",
|
||||
+ "z:1:29: at range loop continue: {{range}} branches end in different contexts",
|
||||
+ },
|
||||
+ {
|
||||
"<a b=1 c={{.H}}",
|
||||
"z: ends in a non-text context: {stateAttr delimSpaceOrTagEnd",
|
||||
},
|
||||
diff --git a/src/text/template/doc.go b/src/text/template/doc.go
|
||||
index 7b30294..0228b15 100644
|
||||
--- a/src/text/template/doc.go
|
||||
+++ b/src/text/template/doc.go
|
||||
@@ -112,6 +112,14 @@ data, defined in detail in the corresponding sections that follow.
|
||||
T0 is executed; otherwise, dot is set to the successive elements
|
||||
of the array, slice, or map and T1 is executed.
|
||||
|
||||
+ {{break}}
|
||||
+ The innermost {{range pipeline}} loop is ended early, stopping the
|
||||
+ current iteration and bypassing all remaining iterations.
|
||||
+
|
||||
+ {{continue}}
|
||||
+ The current iteration of the innermost {{range pipeline}} loop is
|
||||
+ stopped, and the loop starts the next iteration.
|
||||
+
|
||||
{{template "name"}}
|
||||
The template with the specified name is executed with nil data.
|
||||
|
||||
diff --git a/src/text/template/exec.go b/src/text/template/exec.go
|
||||
index 7ac5175..6cb140a 100644
|
||||
--- a/src/text/template/exec.go
|
||||
+++ b/src/text/template/exec.go
|
||||
@@ -5,6 +5,7 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
+ "errors"
|
||||
"fmt"
|
||||
"internal/fmtsort"
|
||||
"io"
|
||||
@@ -244,6 +245,12 @@ func (t *Template) DefinedTemplates() string {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
+// Sentinel errors for use with panic to signal early exits from range loops.
|
||||
+var (
|
||||
+ walkBreak = errors.New("break")
|
||||
+ walkContinue = errors.New("continue")
|
||||
+)
|
||||
+
|
||||
// Walk functions step through the major pieces of the template structure,
|
||||
// generating output as they go.
|
||||
func (s *state) walk(dot reflect.Value, node parse.Node) {
|
||||
@@ -256,7 +263,11 @@ func (s *state) walk(dot reflect.Value, node parse.Node) {
|
||||
if len(node.Pipe.Decl) == 0 {
|
||||
s.printValue(node, val)
|
||||
}
|
||||
+ case *parse.BreakNode:
|
||||
+ panic(walkBreak)
|
||||
case *parse.CommentNode:
|
||||
+ case *parse.ContinueNode:
|
||||
+ panic(walkContinue)
|
||||
case *parse.IfNode:
|
||||
s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList)
|
||||
case *parse.ListNode:
|
||||
@@ -335,6 +346,11 @@ func isTrue(val reflect.Value) (truth, ok bool) {
|
||||
|
||||
func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
|
||||
s.at(r)
|
||||
+ defer func() {
|
||||
+ if r := recover(); r != nil && r != walkBreak {
|
||||
+ panic(r)
|
||||
+ }
|
||||
+ }()
|
||||
defer s.pop(s.mark())
|
||||
val, _ := indirect(s.evalPipeline(dot, r.Pipe))
|
||||
// mark top of stack before any variables in the body are pushed.
|
||||
@@ -348,8 +364,14 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
|
||||
if len(r.Pipe.Decl) > 1 {
|
||||
s.setTopVar(2, index)
|
||||
}
|
||||
+ defer s.pop(mark)
|
||||
+ defer func() {
|
||||
+ // Consume panic(walkContinue)
|
||||
+ if r := recover(); r != nil && r != walkContinue {
|
||||
+ panic(r)
|
||||
+ }
|
||||
+ }()
|
||||
s.walk(elem, r.List)
|
||||
- s.pop(mark)
|
||||
}
|
||||
switch val.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go
|
||||
index 3309b33..a639f44 100644
|
||||
--- a/src/text/template/exec_test.go
|
||||
+++ b/src/text/template/exec_test.go
|
||||
@@ -563,6 +563,8 @@ var execTests = []execTest{
|
||||
{"range empty no else", "{{range .SIEmpty}}-{{.}}-{{end}}", "", tVal, true},
|
||||
{"range []int else", "{{range .SI}}-{{.}}-{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
|
||||
{"range empty else", "{{range .SIEmpty}}-{{.}}-{{else}}EMPTY{{end}}", "EMPTY", tVal, true},
|
||||
+ {"range []int break else", "{{range .SI}}-{{.}}-{{break}}NOTREACHED{{else}}EMPTY{{end}}", "-3-", tVal, true},
|
||||
+ {"range []int continue else", "{{range .SI}}-{{.}}-{{continue}}NOTREACHED{{else}}EMPTY{{end}}", "-3--4--5-", tVal, true},
|
||||
{"range []bool", "{{range .SB}}-{{.}}-{{end}}", "-true--false-", tVal, true},
|
||||
{"range []int method", "{{range .SI | .MAdd .I}}-{{.}}-{{end}}", "-20--21--22-", tVal, true},
|
||||
{"range map", "{{range .MSI}}-{{.}}-{{end}}", "-1--3--2-", tVal, true},
|
||||
diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go
|
||||
index 6784071..95e3377 100644
|
||||
--- a/src/text/template/parse/lex.go
|
||||
+++ b/src/text/template/parse/lex.go
|
||||
@@ -62,6 +62,8 @@ const (
|
||||
// Keywords appear after all the rest.
|
||||
itemKeyword // used only to delimit the keywords
|
||||
itemBlock // block keyword
|
||||
+ itemBreak // break keyword
|
||||
+ itemContinue // continue keyword
|
||||
itemDot // the cursor, spelled '.'
|
||||
itemDefine // define keyword
|
||||
itemElse // else keyword
|
||||
@@ -76,6 +78,8 @@ const (
|
||||
var key = map[string]itemType{
|
||||
".": itemDot,
|
||||
"block": itemBlock,
|
||||
+ "break": itemBreak,
|
||||
+ "continue": itemContinue,
|
||||
"define": itemDefine,
|
||||
"else": itemElse,
|
||||
"end": itemEnd,
|
||||
@@ -119,6 +123,8 @@ type lexer struct {
|
||||
parenDepth int // nesting depth of ( ) exprs
|
||||
line int // 1+number of newlines seen
|
||||
startLine int // start line of this item
|
||||
+ breakOK bool // break keyword allowed
|
||||
+ continueOK bool // continue keyword allowed
|
||||
}
|
||||
|
||||
// next returns the next rune in the input.
|
||||
@@ -461,7 +467,12 @@ Loop:
|
||||
}
|
||||
switch {
|
||||
case key[word] > itemKeyword:
|
||||
- l.emit(key[word])
|
||||
+ item := key[word]
|
||||
+ if item == itemBreak && !l.breakOK || item == itemContinue && !l.continueOK {
|
||||
+ l.emit(itemIdentifier)
|
||||
+ } else {
|
||||
+ l.emit(item)
|
||||
+ }
|
||||
case word[0] == '.':
|
||||
l.emit(itemField)
|
||||
case word == "true", word == "false":
|
||||
diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go
|
||||
index 6510eed..df6aabf 100644
|
||||
--- a/src/text/template/parse/lex_test.go
|
||||
+++ b/src/text/template/parse/lex_test.go
|
||||
@@ -35,6 +35,8 @@ var itemName = map[itemType]string{
|
||||
// keywords
|
||||
itemDot: ".",
|
||||
itemBlock: "block",
|
||||
+ itemBreak: "break",
|
||||
+ itemContinue: "continue",
|
||||
itemDefine: "define",
|
||||
itemElse: "else",
|
||||
itemIf: "if",
|
||||
diff --git a/src/text/template/parse/node.go b/src/text/template/parse/node.go
|
||||
index a9dad5e..c398da0 100644
|
||||
--- a/src/text/template/parse/node.go
|
||||
+++ b/src/text/template/parse/node.go
|
||||
@@ -71,6 +71,8 @@ const (
|
||||
NodeVariable // A $ variable.
|
||||
NodeWith // A with action.
|
||||
NodeComment // A comment.
|
||||
+ NodeBreak // A break action.
|
||||
+ NodeContinue // A continue action.
|
||||
)
|
||||
|
||||
// Nodes.
|
||||
@@ -907,6 +909,40 @@ func (i *IfNode) Copy() Node {
|
||||
return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList())
|
||||
}
|
||||
|
||||
+// BreakNode represents a {{break}} action.
|
||||
+type BreakNode struct {
|
||||
+ tr *Tree
|
||||
+ NodeType
|
||||
+ Pos
|
||||
+ Line int
|
||||
+}
|
||||
+
|
||||
+func (t *Tree) newBreak(pos Pos, line int) *BreakNode {
|
||||
+ return &BreakNode{tr: t, NodeType: NodeBreak, Pos: pos, Line: line}
|
||||
+}
|
||||
+
|
||||
+func (b *BreakNode) Copy() Node { return b.tr.newBreak(b.Pos, b.Line) }
|
||||
+func (b *BreakNode) String() string { return "{{break}}" }
|
||||
+func (b *BreakNode) tree() *Tree { return b.tr }
|
||||
+func (b *BreakNode) writeTo(sb *strings.Builder) { sb.WriteString("{{break}}") }
|
||||
+
|
||||
+// ContinueNode represents a {{continue}} action.
|
||||
+type ContinueNode struct {
|
||||
+ tr *Tree
|
||||
+ NodeType
|
||||
+ Pos
|
||||
+ Line int
|
||||
+}
|
||||
+
|
||||
+func (t *Tree) newContinue(pos Pos, line int) *ContinueNode {
|
||||
+ return &ContinueNode{tr: t, NodeType: NodeContinue, Pos: pos, Line: line}
|
||||
+}
|
||||
+
|
||||
+func (c *ContinueNode) Copy() Node { return c.tr.newContinue(c.Pos, c.Line) }
|
||||
+func (c *ContinueNode) String() string { return "{{continue}}" }
|
||||
+func (c *ContinueNode) tree() *Tree { return c.tr }
|
||||
+func (c *ContinueNode) writeTo(sb *strings.Builder) { sb.WriteString("{{continue}}") }
|
||||
+
|
||||
// RangeNode represents a {{range}} action and its commands.
|
||||
type RangeNode struct {
|
||||
BranchNode
|
||||
diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go
|
||||
index 5e6e512..7f78b56 100644
|
||||
--- a/src/text/template/parse/parse.go
|
||||
+++ b/src/text/template/parse/parse.go
|
||||
@@ -31,6 +31,7 @@ type Tree struct {
|
||||
vars []string // variables defined at the moment.
|
||||
treeSet map[string]*Tree
|
||||
actionLine int // line of left delim starting action
|
||||
+ rangeDepth int
|
||||
mode Mode
|
||||
}
|
||||
|
||||
@@ -223,6 +224,8 @@ func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer, treeSet ma
|
||||
t.vars = []string{"$"}
|
||||
t.funcs = funcs
|
||||
t.treeSet = treeSet
|
||||
+ lex.breakOK = !t.hasFunction("break")
|
||||
+ lex.continueOK = !t.hasFunction("continue")
|
||||
}
|
||||
|
||||
// stopParse terminates parsing.
|
||||
@@ -385,6 +388,10 @@ func (t *Tree) action() (n Node) {
|
||||
switch token := t.nextNonSpace(); token.typ {
|
||||
case itemBlock:
|
||||
return t.blockControl()
|
||||
+ case itemBreak:
|
||||
+ return t.breakControl(token.pos, token.line)
|
||||
+ case itemContinue:
|
||||
+ return t.continueControl(token.pos, token.line)
|
||||
case itemElse:
|
||||
return t.elseControl()
|
||||
case itemEnd:
|
||||
@@ -404,6 +411,32 @@ func (t *Tree) action() (n Node) {
|
||||
return t.newAction(token.pos, token.line, t.pipeline("command", itemRightDelim))
|
||||
}
|
||||
|
||||
+// Break:
|
||||
+// {{break}}
|
||||
+// Break keyword is past.
|
||||
+func (t *Tree) breakControl(pos Pos, line int) Node {
|
||||
+ if token := t.next(); token.typ != itemRightDelim {
|
||||
+ t.unexpected(token, "in {{break}}")
|
||||
+ }
|
||||
+ if t.rangeDepth == 0 {
|
||||
+ t.errorf("{{break}} outside {{range}}")
|
||||
+ }
|
||||
+ return t.newBreak(pos, line)
|
||||
+}
|
||||
+
|
||||
+// Continue:
|
||||
+// {{continue}}
|
||||
+// Continue keyword is past.
|
||||
+func (t *Tree) continueControl(pos Pos, line int) Node {
|
||||
+ if token := t.next(); token.typ != itemRightDelim {
|
||||
+ t.unexpected(token, "in {{continue}}")
|
||||
+ }
|
||||
+ if t.rangeDepth == 0 {
|
||||
+ t.errorf("{{continue}} outside {{range}}")
|
||||
+ }
|
||||
+ return t.newContinue(pos, line)
|
||||
+}
|
||||
+
|
||||
// Pipeline:
|
||||
// declarations? command ('|' command)*
|
||||
func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) {
|
||||
@@ -479,8 +512,14 @@ func (t *Tree) checkPipeline(pipe *PipeNode, context string) {
|
||||
func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) {
|
||||
defer t.popVars(len(t.vars))
|
||||
pipe = t.pipeline(context, itemRightDelim)
|
||||
+ if context == "range" {
|
||||
+ t.rangeDepth++
|
||||
+ }
|
||||
var next Node
|
||||
list, next = t.itemList()
|
||||
+ if context == "range" {
|
||||
+ t.rangeDepth--
|
||||
+ }
|
||||
switch next.Type() {
|
||||
case nodeEnd: //done
|
||||
case nodeElse:
|
||||
@@ -522,7 +561,8 @@ func (t *Tree) ifControl() Node {
|
||||
// {{range pipeline}} itemList {{else}} itemList {{end}}
|
||||
// Range keyword is past.
|
||||
func (t *Tree) rangeControl() Node {
|
||||
- return t.newRange(t.parseControl(false, "range"))
|
||||
+ r := t.newRange(t.parseControl(false, "range"))
|
||||
+ return r
|
||||
}
|
||||
|
||||
// With:
|
||||
diff --git a/src/text/template/parse/parse_test.go b/src/text/template/parse/parse_test.go
|
||||
index 220f984..ba45636 100644
|
||||
--- a/src/text/template/parse/parse_test.go
|
||||
+++ b/src/text/template/parse/parse_test.go
|
||||
@@ -230,6 +230,10 @@ var parseTests = []parseTest{
|
||||
`{{range $x := .SI}}{{.}}{{end}}`},
|
||||
{"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError,
|
||||
`{{range $x, $y := .SI}}{{.}}{{end}}`},
|
||||
+ {"range with break", "{{range .SI}}{{.}}{{break}}{{end}}", noError,
|
||||
+ `{{range .SI}}{{.}}{{break}}{{end}}`},
|
||||
+ {"range with continue", "{{range .SI}}{{.}}{{continue}}{{end}}", noError,
|
||||
+ `{{range .SI}}{{.}}{{continue}}{{end}}`},
|
||||
{"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError,
|
||||
`{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`},
|
||||
{"template", "{{template `x`}}", noError,
|
||||
@@ -279,6 +283,10 @@ var parseTests = []parseTest{
|
||||
{"adjacent args", "{{printf 3`x`}}", hasError, ""},
|
||||
{"adjacent args with .", "{{printf `x`.}}", hasError, ""},
|
||||
{"extra end after if", "{{if .X}}a{{else if .Y}}b{{end}}{{end}}", hasError, ""},
|
||||
+ {"break outside range", "{{range .}}{{end}} {{break}}", hasError, ""},
|
||||
+ {"continue outside range", "{{range .}}{{end}} {{continue}}", hasError, ""},
|
||||
+ {"break in range else", "{{range .}}{{else}}{{break}}{{end}}", hasError, ""},
|
||||
+ {"continue in range else", "{{range .}}{{else}}{{continue}}{{end}}", hasError, ""},
|
||||
// Other kinds of assignments and operators aren't available yet.
|
||||
{"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"},
|
||||
{"bug0b", "{{$x += 1}}{{$x}}", hasError, ""},
|
||||
--
|
||||
2.7.4
|
||||
@@ -1,7 +1,7 @@
|
||||
From 16f4882984569f179d73967c9eee679bb9b098c5 Mon Sep 17 00:00:00 2001
|
||||
From: Roland Shoemaker <bracewell@google.com>
|
||||
Date: Mon, 20 Mar 2023 11:01:13 -0700
|
||||
Subject: [PATCH 3/3] html/template: disallow actions in JS template literals
|
||||
Subject: [PATCH 6/6] html/template: disallow actions in JS template literals
|
||||
|
||||
ECMAScript 6 introduced template literals[0][1] which are delimited with
|
||||
backticks. These need to be escaped in a similar fashion to the
|
||||
@@ -52,12 +52,15 @@ CVE: CVE-2023-24538
|
||||
Signed-off-by: Shubham Kulkarni <skulkarni@mvista.com>
|
||||
---
|
||||
src/html/template/context.go | 2 ++
|
||||
src/html/template/error.go | 13 +++++++++++++
|
||||
src/html/template/escape.go | 11 +++++++++++
|
||||
src/html/template/error.go | 13 ++++++++
|
||||
src/html/template/escape.go | 11 +++++++
|
||||
src/html/template/escape_test.go | 66 ++++++++++++++++++++++-----------------
|
||||
src/html/template/js.go | 2 ++
|
||||
src/html/template/jsctx_string.go | 9 +++++++++
|
||||
src/html/template/transition.go | 7 ++++++-
|
||||
6 files changed, 43 insertions(+), 1 deletion(-)
|
||||
src/html/template/js_test.go | 2 +-
|
||||
src/html/template/jsctx_string.go | 9 ++++++
|
||||
src/html/template/state_string.go | 37 ++++++++++++++++++++--
|
||||
src/html/template/transition.go | 7 ++++-
|
||||
9 files changed, 116 insertions(+), 33 deletions(-)
|
||||
|
||||
diff --git a/src/html/template/context.go b/src/html/template/context.go
|
||||
index f7d4849..0b65313 100644
|
||||
@@ -125,6 +128,104 @@ index f12dafa..29ca5b3 100644
|
||||
case stateJSRegexp:
|
||||
s = append(s, "_html_template_jsregexpescaper")
|
||||
case stateCSS:
|
||||
diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
|
||||
index fa2b84a..1b150e9 100644
|
||||
--- a/src/html/template/escape_test.go
|
||||
+++ b/src/html/template/escape_test.go
|
||||
@@ -681,35 +681,31 @@ func TestEscape(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
- tmpl := New(test.name)
|
||||
- tmpl = Must(tmpl.Parse(test.input))
|
||||
- // Check for bug 6459: Tree field was not set in Parse.
|
||||
- if tmpl.Tree != tmpl.text.Tree {
|
||||
- t.Errorf("%s: tree not set properly", test.name)
|
||||
- continue
|
||||
- }
|
||||
- b := new(bytes.Buffer)
|
||||
- if err := tmpl.Execute(b, data); err != nil {
|
||||
- t.Errorf("%s: template execution failed: %s", test.name, err)
|
||||
- continue
|
||||
- }
|
||||
- if w, g := test.output, b.String(); w != g {
|
||||
- t.Errorf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g)
|
||||
- continue
|
||||
- }
|
||||
- b.Reset()
|
||||
- if err := tmpl.Execute(b, pdata); err != nil {
|
||||
- t.Errorf("%s: template execution failed for pointer: %s", test.name, err)
|
||||
- continue
|
||||
- }
|
||||
- if w, g := test.output, b.String(); w != g {
|
||||
- t.Errorf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g)
|
||||
- continue
|
||||
- }
|
||||
- if tmpl.Tree != tmpl.text.Tree {
|
||||
- t.Errorf("%s: tree mismatch", test.name)
|
||||
- continue
|
||||
- }
|
||||
+ t.Run(test.name, func(t *testing.T) {
|
||||
+ tmpl := New(test.name)
|
||||
+ tmpl = Must(tmpl.Parse(test.input))
|
||||
+ // Check for bug 6459: Tree field was not set in Parse.
|
||||
+ if tmpl.Tree != tmpl.text.Tree {
|
||||
+ t.Fatalf("%s: tree not set properly", test.name)
|
||||
+ }
|
||||
+ b := new(strings.Builder)
|
||||
+ if err := tmpl.Execute(b, data); err != nil {
|
||||
+ t.Fatalf("%s: template execution failed: %s", test.name, err)
|
||||
+ }
|
||||
+ if w, g := test.output, b.String(); w != g {
|
||||
+ t.Fatalf("%s: escaped output: want\n\t%q\ngot\n\t%q", test.name, w, g)
|
||||
+ }
|
||||
+ b.Reset()
|
||||
+ if err := tmpl.Execute(b, pdata); err != nil {
|
||||
+ t.Fatalf("%s: template execution failed for pointer: %s", test.name, err)
|
||||
+ }
|
||||
+ if w, g := test.output, b.String(); w != g {
|
||||
+ t.Fatalf("%s: escaped output for pointer: want\n\t%q\ngot\n\t%q", test.name, w, g)
|
||||
+ }
|
||||
+ if tmpl.Tree != tmpl.text.Tree {
|
||||
+ t.Fatalf("%s: tree mismatch", test.name)
|
||||
+ }
|
||||
+ })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -936,6 +932,10 @@ func TestErrors(t *testing.T) {
|
||||
"{{range .Items}}<a{{if .X}}{{end}}>{{if .X}}{{break}}{{end}}{{end}}",
|
||||
"",
|
||||
},
|
||||
+ {
|
||||
+ "<script>var a = `${a+b}`</script>`",
|
||||
+ "",
|
||||
+ },
|
||||
// Error cases.
|
||||
{
|
||||
"{{if .Cond}}<a{{end}}",
|
||||
@@ -1082,6 +1082,10 @@ func TestErrors(t *testing.T) {
|
||||
// html is allowed since it is the last command in the pipeline, but urlquery is not.
|
||||
`predefined escaper "urlquery" disallowed in template`,
|
||||
},
|
||||
+ {
|
||||
+ "<script>var tmpl = `asd {{.}}`;</script>",
|
||||
+ `{{.}} appears in a JS template literal`,
|
||||
+ },
|
||||
}
|
||||
for _, test := range tests {
|
||||
buf := new(bytes.Buffer)
|
||||
@@ -1304,6 +1308,10 @@ func TestEscapeText(t *testing.T) {
|
||||
context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript},
|
||||
},
|
||||
{
|
||||
+ "<a onclick=\"`foo",
|
||||
+ context{state: stateJSBqStr, delim: delimDoubleQuote, attr: attrScript},
|
||||
+ },
|
||||
+ {
|
||||
`<A ONCLICK="'`,
|
||||
context{state: stateJSSqStr, delim: delimDoubleQuote, attr: attrScript},
|
||||
},
|
||||
diff --git a/src/html/template/js.go b/src/html/template/js.go
|
||||
index ea9c183..b888eaf 100644
|
||||
--- a/src/html/template/js.go
|
||||
@@ -145,6 +246,19 @@ index ea9c183..b888eaf 100644
|
||||
'+': `\u002b`,
|
||||
'/': `\/`,
|
||||
'<': `\u003c`,
|
||||
diff --git a/src/html/template/js_test.go b/src/html/template/js_test.go
|
||||
index d7ee47b..7d963ae 100644
|
||||
--- a/src/html/template/js_test.go
|
||||
+++ b/src/html/template/js_test.go
|
||||
@@ -292,7 +292,7 @@ func TestEscapersOnLower7AndSelectHighCodepoints(t *testing.T) {
|
||||
`0123456789:;\u003c=\u003e?` +
|
||||
`@ABCDEFGHIJKLMNO` +
|
||||
`PQRSTUVWXYZ[\\]^_` +
|
||||
- "`abcdefghijklmno" +
|
||||
+ "\\u0060abcdefghijklmno" +
|
||||
"pqrstuvwxyz{|}~\u007f" +
|
||||
"\u00A0\u0100\\u2028\\u2029\ufeff\U0001D11E",
|
||||
},
|
||||
diff --git a/src/html/template/jsctx_string.go b/src/html/template/jsctx_string.go
|
||||
index dd1d87e..2394893 100644
|
||||
--- a/src/html/template/jsctx_string.go
|
||||
@@ -165,6 +279,55 @@ index dd1d87e..2394893 100644
|
||||
const _jsCtx_name = "jsCtxRegexpjsCtxDivOpjsCtxUnknown"
|
||||
|
||||
var _jsCtx_index = [...]uint8{0, 11, 21, 33}
|
||||
diff --git a/src/html/template/state_string.go b/src/html/template/state_string.go
|
||||
index 05104be..6fb1a6e 100644
|
||||
--- a/src/html/template/state_string.go
|
||||
+++ b/src/html/template/state_string.go
|
||||
@@ -4,9 +4,42 @@ package template
|
||||
|
||||
import "strconv"
|
||||
|
||||
-const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateError"
|
||||
+func _() {
|
||||
+ // An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
+ // Re-run the stringer command to generate them again.
|
||||
+ var x [1]struct{}
|
||||
+ _ = x[stateText-0]
|
||||
+ _ = x[stateTag-1]
|
||||
+ _ = x[stateAttrName-2]
|
||||
+ _ = x[stateAfterName-3]
|
||||
+ _ = x[stateBeforeValue-4]
|
||||
+ _ = x[stateHTMLCmt-5]
|
||||
+ _ = x[stateRCDATA-6]
|
||||
+ _ = x[stateAttr-7]
|
||||
+ _ = x[stateURL-8]
|
||||
+ _ = x[stateSrcset-9]
|
||||
+ _ = x[stateJS-10]
|
||||
+ _ = x[stateJSDqStr-11]
|
||||
+ _ = x[stateJSSqStr-12]
|
||||
+ _ = x[stateJSBqStr-13]
|
||||
+ _ = x[stateJSRegexp-14]
|
||||
+ _ = x[stateJSBlockCmt-15]
|
||||
+ _ = x[stateJSLineCmt-16]
|
||||
+ _ = x[stateCSS-17]
|
||||
+ _ = x[stateCSSDqStr-18]
|
||||
+ _ = x[stateCSSSqStr-19]
|
||||
+ _ = x[stateCSSDqURL-20]
|
||||
+ _ = x[stateCSSSqURL-21]
|
||||
+ _ = x[stateCSSURL-22]
|
||||
+ _ = x[stateCSSBlockCmt-23]
|
||||
+ _ = x[stateCSSLineCmt-24]
|
||||
+ _ = x[stateError-25]
|
||||
+ _ = x[stateDead-26]
|
||||
+}
|
||||
+
|
||||
+const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead"
|
||||
|
||||
-var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 155, 170, 184, 192, 205, 218, 231, 244, 255, 271, 286, 296}
|
||||
+var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 154, 167, 182, 196, 204, 217, 230, 243, 256, 267, 283, 298, 308, 317}
|
||||
|
||||
func (i state) String() string {
|
||||
if i >= state(len(_state_index)-1) {
|
||||
diff --git a/src/html/template/transition.go b/src/html/template/transition.go
|
||||
index 06df679..92eb351 100644
|
||||
--- a/src/html/template/transition.go
|
||||
262
meta/recipes-devtools/go/go-1.14/CVE-2023-39318.patch
Normal file
262
meta/recipes-devtools/go/go-1.14/CVE-2023-39318.patch
Normal file
@@ -0,0 +1,262 @@
|
||||
From 023b542edf38e2a1f87fcefb9f75ff2f99401b4c Mon Sep 17 00:00:00 2001
|
||||
From: Roland Shoemaker <bracewell@google.com>
|
||||
Date: Thu, 3 Aug 2023 12:24:13 -0700
|
||||
Subject: [PATCH] [release-branch.go1.20] html/template: support HTML-like
|
||||
comments in script contexts
|
||||
|
||||
Per Appendix B.1.1 of the ECMAScript specification, support HTML-like
|
||||
comments in script contexts. Also per section 12.5, support hashbang
|
||||
comments. This brings our parsing in-line with how browsers treat these
|
||||
comment types.
|
||||
|
||||
Thanks to Takeshi Kaneko (GMO Cybersecurity by Ierae, Inc.) for
|
||||
reporting this issue.
|
||||
|
||||
Fixes #62196
|
||||
Fixes #62395
|
||||
Fixes CVE-2023-39318
|
||||
|
||||
Change-Id: Id512702c5de3ae46cf648e268cb10e1eb392a181
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1976593
|
||||
Run-TryBot: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2014620
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/526098
|
||||
Run-TryBot: Cherry Mui <cherryyz@google.com>
|
||||
TryBot-Result: Gopher Robot <gobot@golang.org>
|
||||
|
||||
Upstream-Status: Backport from [https://github.com/golang/go/commit/023b542edf38e2a1f87fcefb9f75ff2f99401b4c]
|
||||
CVE: CVE-2023-39318
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
src/html/template/context.go | 6 ++-
|
||||
src/html/template/escape.go | 5 +-
|
||||
src/html/template/escape_test.go | 10 ++++
|
||||
src/html/template/state_string.go | 26 +++++-----
|
||||
src/html/template/transition.go | 80 ++++++++++++++++++++-----------
|
||||
5 files changed, 84 insertions(+), 43 deletions(-)
|
||||
|
||||
diff --git a/src/html/template/context.go b/src/html/template/context.go
|
||||
index 0b65313..4eb7891 100644
|
||||
--- a/src/html/template/context.go
|
||||
+++ b/src/html/template/context.go
|
||||
@@ -124,6 +124,10 @@ const (
|
||||
stateJSBlockCmt
|
||||
// stateJSLineCmt occurs inside a JavaScript // line comment.
|
||||
stateJSLineCmt
|
||||
+ // stateJSHTMLOpenCmt occurs inside a JavaScript <!-- HTML-like comment.
|
||||
+ stateJSHTMLOpenCmt
|
||||
+ // stateJSHTMLCloseCmt occurs inside a JavaScript --> HTML-like comment.
|
||||
+ stateJSHTMLCloseCmt
|
||||
// stateCSS occurs inside a <style> element or style attribute.
|
||||
stateCSS
|
||||
// stateCSSDqStr occurs inside a CSS double quoted string.
|
||||
@@ -149,7 +153,7 @@ const (
|
||||
// authors & maintainers, not for end-users or machines.
|
||||
func isComment(s state) bool {
|
||||
switch s {
|
||||
- case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateCSSBlockCmt, stateCSSLineCmt:
|
||||
+ case stateHTMLCmt, stateJSBlockCmt, stateJSLineCmt, stateJSHTMLOpenCmt, stateJSHTMLCloseCmt, stateCSSBlockCmt, stateCSSLineCmt:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
diff --git a/src/html/template/escape.go b/src/html/template/escape.go
|
||||
index 435f912..ad2ec69 100644
|
||||
--- a/src/html/template/escape.go
|
||||
+++ b/src/html/template/escape.go
|
||||
@@ -698,9 +698,12 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context {
|
||||
if c.state != c1.state && isComment(c1.state) && c1.delim == delimNone {
|
||||
// Preserve the portion between written and the comment start.
|
||||
cs := i1 - 2
|
||||
- if c1.state == stateHTMLCmt {
|
||||
+ if c1.state == stateHTMLCmt || c1.state == stateJSHTMLOpenCmt {
|
||||
// "<!--" instead of "/*" or "//"
|
||||
cs -= 2
|
||||
+ } else if c1.state == stateJSHTMLCloseCmt {
|
||||
+ // "-->" instead of "/*" or "//"
|
||||
+ cs -= 1
|
||||
}
|
||||
b.Write(s[written:cs])
|
||||
written = i1
|
||||
diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
|
||||
index f550691..5f41e52 100644
|
||||
--- a/src/html/template/escape_test.go
|
||||
+++ b/src/html/template/escape_test.go
|
||||
@@ -503,6 +503,16 @@ func TestEscape(t *testing.T) {
|
||||
"<script>var a/*b*///c\nd</script>",
|
||||
"<script>var a \nd</script>",
|
||||
},
|
||||
+ {
|
||||
+ "JS HTML-like comments",
|
||||
+ "<script>before <!-- beep\nbetween\nbefore-->boop\n</script>",
|
||||
+ "<script>before \nbetween\nbefore\n</script>",
|
||||
+ },
|
||||
+ {
|
||||
+ "JS hashbang comment",
|
||||
+ "<script>#! beep\n</script>",
|
||||
+ "<script>\n</script>",
|
||||
+ },
|
||||
{
|
||||
"CSS comments",
|
||||
"<style>p// paragraph\n" +
|
||||
diff --git a/src/html/template/state_string.go b/src/html/template/state_string.go
|
||||
index 05104be..b5cfe70 100644
|
||||
--- a/src/html/template/state_string.go
|
||||
+++ b/src/html/template/state_string.go
|
||||
@@ -25,21 +25,23 @@ func _() {
|
||||
_ = x[stateJSRegexp-14]
|
||||
_ = x[stateJSBlockCmt-15]
|
||||
_ = x[stateJSLineCmt-16]
|
||||
- _ = x[stateCSS-17]
|
||||
- _ = x[stateCSSDqStr-18]
|
||||
- _ = x[stateCSSSqStr-19]
|
||||
- _ = x[stateCSSDqURL-20]
|
||||
- _ = x[stateCSSSqURL-21]
|
||||
- _ = x[stateCSSURL-22]
|
||||
- _ = x[stateCSSBlockCmt-23]
|
||||
- _ = x[stateCSSLineCmt-24]
|
||||
- _ = x[stateError-25]
|
||||
- _ = x[stateDead-26]
|
||||
+ _ = x[stateJSHTMLOpenCmt-17]
|
||||
+ _ = x[stateJSHTMLCloseCmt-18]
|
||||
+ _ = x[stateCSS-19]
|
||||
+ _ = x[stateCSSDqStr-20]
|
||||
+ _ = x[stateCSSSqStr-21]
|
||||
+ _ = x[stateCSSDqURL-22]
|
||||
+ _ = x[stateCSSSqURL-23]
|
||||
+ _ = x[stateCSSURL-24]
|
||||
+ _ = x[stateCSSBlockCmt-25]
|
||||
+ _ = x[stateCSSLineCmt-26]
|
||||
+ _ = x[stateError-27]
|
||||
+ _ = x[stateDead-28]
|
||||
}
|
||||
|
||||
-const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead"
|
||||
+const _state_name = "stateTextstateTagstateAttrNamestateAfterNamestateBeforeValuestateHTMLCmtstateRCDATAstateAttrstateURLstateSrcsetstateJSstateJSDqStrstateJSSqStrstateJSBqStrstateJSRegexpstateJSBlockCmtstateJSLineCmtstateJSHTMLOpenCmtstateJSHTMLCloseCmtstateCSSstateCSSDqStrstateCSSSqStrstateCSSDqURLstateCSSSqURLstateCSSURLstateCSSBlockCmtstateCSSLineCmtstateErrorstateDead"
|
||||
|
||||
-var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 154, 167, 182, 196, 204, 217, 230, 243, 256, 267, 283, 298, 308, 317}
|
||||
+var _state_index = [...]uint16{0, 9, 17, 30, 44, 60, 72, 83, 92, 100, 111, 118, 130, 142, 154, 167, 182, 196, 214, 233, 241, 254, 267, 280, 293, 304, 320, 335, 345, 354}
|
||||
|
||||
func (i state) String() string {
|
||||
if i >= state(len(_state_index)-1) {
|
||||
diff --git a/src/html/template/transition.go b/src/html/template/transition.go
|
||||
index 92eb351..12aa4c4 100644
|
||||
--- a/src/html/template/transition.go
|
||||
+++ b/src/html/template/transition.go
|
||||
@@ -14,32 +14,34 @@ import (
|
||||
// the updated context and the number of bytes consumed from the front of the
|
||||
// input.
|
||||
var transitionFunc = [...]func(context, []byte) (context, int){
|
||||
- stateText: tText,
|
||||
- stateTag: tTag,
|
||||
- stateAttrName: tAttrName,
|
||||
- stateAfterName: tAfterName,
|
||||
- stateBeforeValue: tBeforeValue,
|
||||
- stateHTMLCmt: tHTMLCmt,
|
||||
- stateRCDATA: tSpecialTagEnd,
|
||||
- stateAttr: tAttr,
|
||||
- stateURL: tURL,
|
||||
- stateSrcset: tURL,
|
||||
- stateJS: tJS,
|
||||
- stateJSDqStr: tJSDelimited,
|
||||
- stateJSSqStr: tJSDelimited,
|
||||
- stateJSBqStr: tJSDelimited,
|
||||
- stateJSRegexp: tJSDelimited,
|
||||
- stateJSBlockCmt: tBlockCmt,
|
||||
- stateJSLineCmt: tLineCmt,
|
||||
- stateCSS: tCSS,
|
||||
- stateCSSDqStr: tCSSStr,
|
||||
- stateCSSSqStr: tCSSStr,
|
||||
- stateCSSDqURL: tCSSStr,
|
||||
- stateCSSSqURL: tCSSStr,
|
||||
- stateCSSURL: tCSSStr,
|
||||
- stateCSSBlockCmt: tBlockCmt,
|
||||
- stateCSSLineCmt: tLineCmt,
|
||||
- stateError: tError,
|
||||
+ stateText: tText,
|
||||
+ stateTag: tTag,
|
||||
+ stateAttrName: tAttrName,
|
||||
+ stateAfterName: tAfterName,
|
||||
+ stateBeforeValue: tBeforeValue,
|
||||
+ stateHTMLCmt: tHTMLCmt,
|
||||
+ stateRCDATA: tSpecialTagEnd,
|
||||
+ stateAttr: tAttr,
|
||||
+ stateURL: tURL,
|
||||
+ stateSrcset: tURL,
|
||||
+ stateJS: tJS,
|
||||
+ stateJSDqStr: tJSDelimited,
|
||||
+ stateJSSqStr: tJSDelimited,
|
||||
+ stateJSBqStr: tJSDelimited,
|
||||
+ stateJSRegexp: tJSDelimited,
|
||||
+ stateJSBlockCmt: tBlockCmt,
|
||||
+ stateJSLineCmt: tLineCmt,
|
||||
+ stateJSHTMLOpenCmt: tLineCmt,
|
||||
+ stateJSHTMLCloseCmt: tLineCmt,
|
||||
+ stateCSS: tCSS,
|
||||
+ stateCSSDqStr: tCSSStr,
|
||||
+ stateCSSSqStr: tCSSStr,
|
||||
+ stateCSSDqURL: tCSSStr,
|
||||
+ stateCSSSqURL: tCSSStr,
|
||||
+ stateCSSURL: tCSSStr,
|
||||
+ stateCSSBlockCmt: tBlockCmt,
|
||||
+ stateCSSLineCmt: tLineCmt,
|
||||
+ stateError: tError,
|
||||
}
|
||||
|
||||
var commentStart = []byte("<!--")
|
||||
@@ -263,7 +265,7 @@ func tURL(c context, s []byte) (context, int) {
|
||||
|
||||
// tJS is the context transition function for the JS state.
|
||||
func tJS(c context, s []byte) (context, int) {
|
||||
- i := bytes.IndexAny(s, "\"`'/")
|
||||
+ i := bytes.IndexAny(s, "\"`'/<-#")
|
||||
if i == -1 {
|
||||
// Entire input is non string, comment, regexp tokens.
|
||||
c.jsCtx = nextJSCtx(s, c.jsCtx)
|
||||
@@ -293,6 +295,26 @@ func tJS(c context, s []byte) (context, int) {
|
||||
err: errorf(ErrSlashAmbig, nil, 0, "'/' could start a division or regexp: %.32q", s[i:]),
|
||||
}, len(s)
|
||||
}
|
||||
+ // ECMAScript supports HTML style comments for legacy reasons, see Appendix
|
||||
+ // B.1.1 "HTML-like Comments". The handling of these comments is somewhat
|
||||
+ // confusing. Multi-line comments are not supported, i.e. anything on lines
|
||||
+ // between the opening and closing tokens is not considered a comment, but
|
||||
+ // anything following the opening or closing token, on the same line, is
|
||||
+ // ignored. As such we simply treat any line prefixed with "<!--" or "-->"
|
||||
+ // as if it were actually prefixed with "//" and move on.
|
||||
+ case '<':
|
||||
+ if i+3 < len(s) && bytes.Equal(commentStart, s[i:i+4]) {
|
||||
+ c.state, i = stateJSHTMLOpenCmt, i+3
|
||||
+ }
|
||||
+ case '-':
|
||||
+ if i+2 < len(s) && bytes.Equal(commentEnd, s[i:i+3]) {
|
||||
+ c.state, i = stateJSHTMLCloseCmt, i+2
|
||||
+ }
|
||||
+ // ECMAScript also supports "hashbang" comment lines, see Section 12.5.
|
||||
+ case '#':
|
||||
+ if i+1 < len(s) && s[i+1] == '!' {
|
||||
+ c.state, i = stateJSLineCmt, i+1
|
||||
+ }
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
@@ -372,12 +394,12 @@ func tBlockCmt(c context, s []byte) (context, int) {
|
||||
return c, i + 2
|
||||
}
|
||||
|
||||
-// tLineCmt is the context transition function for //comment states.
|
||||
+// tLineCmt is the context transition function for //comment states, and the JS HTML-like comment state.
|
||||
func tLineCmt(c context, s []byte) (context, int) {
|
||||
var lineTerminators string
|
||||
var endState state
|
||||
switch c.state {
|
||||
- case stateJSLineCmt:
|
||||
+ case stateJSLineCmt, stateJSHTMLOpenCmt, stateJSHTMLCloseCmt:
|
||||
lineTerminators, endState = "\n\r\u2028\u2029", stateJS
|
||||
case stateCSSLineCmt:
|
||||
lineTerminators, endState = "\n\f\r", stateCSS
|
||||
--
|
||||
2.24.4
|
||||
|
||||
230
meta/recipes-devtools/go/go-1.14/CVE-2023-39319.patch
Normal file
230
meta/recipes-devtools/go/go-1.14/CVE-2023-39319.patch
Normal file
@@ -0,0 +1,230 @@
|
||||
From 2070531d2f53df88e312edace6c8dfc9686ab2f5 Mon Sep 17 00:00:00 2001
|
||||
From: Roland Shoemaker <bracewell@google.com>
|
||||
Date: Thu, 3 Aug 2023 12:28:28 -0700
|
||||
Subject: [PATCH] [release-branch.go1.20] html/template: properly handle
|
||||
special tags within the script context
|
||||
|
||||
The HTML specification has incredibly complex rules for how to handle
|
||||
"<!--", "<script", and "</script" when they appear within literals in
|
||||
the script context. Rather than attempting to apply these restrictions
|
||||
(which require a significantly more complex state machine) we apply
|
||||
the workaround suggested in section 4.12.1.3 of the HTML specification [1].
|
||||
|
||||
More precisely, when "<!--", "<script", and "</script" appear within
|
||||
literals (strings and regular expressions, ignoring comments since we
|
||||
already elide their content) we replace the "<" with "\x3C". This avoids
|
||||
the unintuitive behavior that using these tags within literals can cause,
|
||||
by simply preventing the rendered content from triggering it. This may
|
||||
break some correct usages of these tags, but on balance is more likely
|
||||
to prevent XSS attacks where users are unknowingly either closing or not
|
||||
closing the script blocks where they think they are.
|
||||
|
||||
Thanks to Takeshi Kaneko (GMO Cybersecurity by Ierae, Inc.) for
|
||||
reporting this issue.
|
||||
|
||||
Fixes #62197
|
||||
Fixes #62397
|
||||
Fixes CVE-2023-39319
|
||||
|
||||
[1] https://html.spec.whatwg.org/#restrictions-for-contents-of-script-elements
|
||||
|
||||
Change-Id: Iab57b0532694827e3eddf57a7497ba1fab1746dc
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/1976594
|
||||
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
|
||||
Reviewed-by: Tatiana Bradley <tatianabradley@google.com>
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Run-TryBot: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-on: https://team-review.git.corp.google.com/c/golang/go-private/+/2014621
|
||||
TryBot-Result: Security TryBots <security-trybots@go-security-trybots.iam.gserviceaccount.com>
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/526099
|
||||
TryBot-Result: Gopher Robot <gobot@golang.org>
|
||||
Run-TryBot: Cherry Mui <cherryyz@google.com>
|
||||
|
||||
Upstream-Status: Backport from [https://github.com/golang/go/commit/2070531d2f53df88e312edace6c8dfc9686ab2f5]
|
||||
CVE: CVE-2023-39319
|
||||
Signed-off-by: Siddharth Doshi <sdoshi@mvista.com>
|
||||
---
|
||||
src/html/template/context.go | 14 ++++++++++
|
||||
src/html/template/escape.go | 26 ++++++++++++++++++
|
||||
src/html/template/escape_test.go | 47 +++++++++++++++++++++++++++++++-
|
||||
src/html/template/transition.go | 15 ++++++++++
|
||||
4 files changed, 101 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/html/template/context.go b/src/html/template/context.go
|
||||
index 4eb7891..feb6517 100644
|
||||
--- a/src/html/template/context.go
|
||||
+++ b/src/html/template/context.go
|
||||
@@ -168,6 +168,20 @@ func isInTag(s state) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
+// isInScriptLiteral returns true if s is one of the literal states within a
|
||||
+// <script> tag, and as such occurances of "<!--", "<script", and "</script"
|
||||
+// need to be treated specially.
|
||||
+func isInScriptLiteral(s state) bool {
|
||||
+ // Ignore the comment states (stateJSBlockCmt, stateJSLineCmt,
|
||||
+ // stateJSHTMLOpenCmt, stateJSHTMLCloseCmt) because their content is already
|
||||
+ // omitted from the output.
|
||||
+ switch s {
|
||||
+ case stateJSDqStr, stateJSSqStr, stateJSBqStr, stateJSRegexp:
|
||||
+ return true
|
||||
+ }
|
||||
+ return false
|
||||
+}
|
||||
+
|
||||
// delim is the delimiter that will end the current HTML attribute.
|
||||
type delim uint8
|
||||
|
||||
diff --git a/src/html/template/escape.go b/src/html/template/escape.go
|
||||
index ad2ec69..de8cf6f 100644
|
||||
--- a/src/html/template/escape.go
|
||||
+++ b/src/html/template/escape.go
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"html"
|
||||
"internal/godebug"
|
||||
"io"
|
||||
+ "regexp"
|
||||
"text/template"
|
||||
"text/template/parse"
|
||||
)
|
||||
@@ -650,6 +651,26 @@ var delimEnds = [...]string{
|
||||
delimSpaceOrTagEnd: " \t\n\f\r>",
|
||||
}
|
||||
|
||||
+var (
|
||||
+ // Per WHATWG HTML specification, section 4.12.1.3, there are extremely
|
||||
+ // complicated rules for how to handle the set of opening tags <!--,
|
||||
+ // <script, and </script when they appear in JS literals (i.e. strings,
|
||||
+ // regexs, and comments). The specification suggests a simple solution,
|
||||
+ // rather than implementing the arcane ABNF, which involves simply escaping
|
||||
+ // the opening bracket with \x3C. We use the below regex for this, since it
|
||||
+ // makes doing the case-insensitive find-replace much simpler.
|
||||
+ specialScriptTagRE = regexp.MustCompile("(?i)<(script|/script|!--)")
|
||||
+ specialScriptTagReplacement = []byte("\\x3C$1")
|
||||
+)
|
||||
+
|
||||
+func containsSpecialScriptTag(s []byte) bool {
|
||||
+ return specialScriptTagRE.Match(s)
|
||||
+}
|
||||
+
|
||||
+func escapeSpecialScriptTags(s []byte) []byte {
|
||||
+ return specialScriptTagRE.ReplaceAll(s, specialScriptTagReplacement)
|
||||
+}
|
||||
+
|
||||
var doctypeBytes = []byte("<!DOCTYPE")
|
||||
|
||||
// escapeText escapes a text template node.
|
||||
@@ -708,6 +729,11 @@ func (e *escaper) escapeText(c context, n *parse.TextNode) context {
|
||||
b.Write(s[written:cs])
|
||||
written = i1
|
||||
}
|
||||
+ if isInScriptLiteral(c.state) && containsSpecialScriptTag(s[i:i1]) {
|
||||
+ b.Write(s[written:i])
|
||||
+ b.Write(escapeSpecialScriptTags(s[i:i1]))
|
||||
+ written = i1
|
||||
+ }
|
||||
if i == i1 && c.state == c1.state {
|
||||
panic(fmt.Sprintf("infinite loop from %v to %v on %q..%q", c, c1, s[:i], s[i:]))
|
||||
}
|
||||
diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go
|
||||
index 5f41e52..0cacb20 100644
|
||||
--- a/src/html/template/escape_test.go
|
||||
+++ b/src/html/template/escape_test.go
|
||||
@@ -513,6 +513,21 @@ func TestEscape(t *testing.T) {
|
||||
"<script>#! beep\n</script>",
|
||||
"<script>\n</script>",
|
||||
},
|
||||
+ {
|
||||
+ "Special tags in <script> string literals",
|
||||
+ `<script>var a = "asd < 123 <!-- 456 < fgh <script jkl < 789 </script"</script>`,
|
||||
+ `<script>var a = "asd < 123 \x3C!-- 456 < fgh \x3Cscript jkl < 789 \x3C/script"</script>`,
|
||||
+ },
|
||||
+ {
|
||||
+ "Special tags in <script> string literals (mixed case)",
|
||||
+ `<script>var a = "<!-- <ScripT </ScripT"</script>`,
|
||||
+ `<script>var a = "\x3C!-- \x3CScripT \x3C/ScripT"</script>`,
|
||||
+ },
|
||||
+ {
|
||||
+ "Special tags in <script> regex literals (mixed case)",
|
||||
+ `<script>var a = /<!-- <ScripT </ScripT/</script>`,
|
||||
+ `<script>var a = /\x3C!-- \x3CScripT \x3C/ScripT/</script>`,
|
||||
+ },
|
||||
{
|
||||
"CSS comments",
|
||||
"<style>p// paragraph\n" +
|
||||
@@ -1501,8 +1516,38 @@ func TestEscapeText(t *testing.T) {
|
||||
context{state: stateJS, element: elementScript},
|
||||
},
|
||||
{
|
||||
+ // <script and </script tags are escaped, so </script> should not
|
||||
+ // cause us to exit the JS state.
|
||||
`<script>document.write("<script>alert(1)</script>");`,
|
||||
- context{state: stateText},
|
||||
+ context{state: stateJS, element: elementScript},
|
||||
+ },
|
||||
+ {
|
||||
+ `<script>document.write("<script>`,
|
||||
+ context{state: stateJSDqStr, element: elementScript},
|
||||
+ },
|
||||
+ {
|
||||
+ `<script>document.write("<script>alert(1)</script>`,
|
||||
+ context{state: stateJSDqStr, element: elementScript},
|
||||
+ },
|
||||
+ {
|
||||
+ `<script>document.write("<script>alert(1)<!--`,
|
||||
+ context{state: stateJSDqStr, element: elementScript},
|
||||
+ },
|
||||
+ {
|
||||
+ `<script>document.write("<script>alert(1)</Script>");`,
|
||||
+ context{state: stateJS, element: elementScript},
|
||||
+ },
|
||||
+ {
|
||||
+ `<script>document.write("<!--");`,
|
||||
+ context{state: stateJS, element: elementScript},
|
||||
+ },
|
||||
+ {
|
||||
+ `<script>let a = /</script`,
|
||||
+ context{state: stateJSRegexp, element: elementScript},
|
||||
+ },
|
||||
+ {
|
||||
+ `<script>let a = /</script/`,
|
||||
+ context{state: stateJS, element: elementScript, jsCtx: jsCtxDivOp},
|
||||
},
|
||||
{
|
||||
`<script type="text/template">`,
|
||||
diff --git a/src/html/template/transition.go b/src/html/template/transition.go
|
||||
index 12aa4c4..3d2a37c 100644
|
||||
--- a/src/html/template/transition.go
|
||||
+++ b/src/html/template/transition.go
|
||||
@@ -214,6 +214,11 @@ var (
|
||||
// element states.
|
||||
func tSpecialTagEnd(c context, s []byte) (context, int) {
|
||||
if c.element != elementNone {
|
||||
+ // script end tags ("</script") within script literals are ignored, so that
|
||||
+ // we can properly escape them.
|
||||
+ if c.element == elementScript && (isInScriptLiteral(c.state) || isComment(c.state)) {
|
||||
+ return c, len(s)
|
||||
+ }
|
||||
if i := indexTagEnd(s, specialTagEndMarkers[c.element]); i != -1 {
|
||||
return context{}, i
|
||||
}
|
||||
@@ -353,6 +358,16 @@ func tJSDelimited(c context, s []byte) (context, int) {
|
||||
inCharset = true
|
||||
case ']':
|
||||
inCharset = false
|
||||
+ case '/':
|
||||
+ // If "</script" appears in a regex literal, the '/' should not
|
||||
+ // close the regex literal, and it will later be escaped to
|
||||
+ // "\x3C/script" in escapeText.
|
||||
+ if i > 0 && i+7 <= len(s) && bytes.Compare(bytes.ToLower(s[i-1:i+7]), []byte("</script")) == 0 {
|
||||
+ i++
|
||||
+ } else if !inCharset {
|
||||
+ c.state, c.jsCtx = stateJS, jsCtxDivOp
|
||||
+ return c, i + 1
|
||||
+ }
|
||||
default:
|
||||
// end delimiter
|
||||
if !inCharset {
|
||||
--
|
||||
2.24.4
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
From bb4e42ad3a0cdd23a1d1797e6299c76b474867c0 Mon Sep 17 00:00:00 2001
|
||||
From 81d6519499dcfebe7d21e65e002a8885a4e8d852 Mon Sep 17 00:00:00 2001
|
||||
From: Joshua Watt <JPEWhacker@gmail.com>
|
||||
Date: Tue, 19 Nov 2019 13:12:17 -0600
|
||||
Subject: [PATCH] Add --debug-prefix-map option
|
||||
@@ -11,7 +11,7 @@ Upstream-Status: Submitted [https://bugzilla.nasm.us/show_bug.cgi?id=3392635]
|
||||
Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
|
||||
|
||||
---
|
||||
asm/nasm.c | 26 +++++++++++++++++++++++++-
|
||||
asm/nasm.c | 24 ++++++++++++++++++++++++
|
||||
include/nasmlib.h | 9 +++++++++
|
||||
nasm.txt | 4 ++++
|
||||
nasmlib/filename.c | 20 ++++++++++++++++++++
|
||||
@@ -23,34 +23,32 @@ Signed-off-by: Joshua Watt <JPEWhacker@gmail.com>
|
||||
stdlib/strlcat.c | 2 +-
|
||||
test/elfdebugprefix.asm | 6 ++++++
|
||||
test/performtest.pl | 12 ++++++++++--
|
||||
12 files changed, 83 insertions(+), 10 deletions(-)
|
||||
12 files changed, 82 insertions(+), 9 deletions(-)
|
||||
create mode 100644 test/elfdebugprefix.asm
|
||||
|
||||
diff --git a/asm/nasm.c b/asm/nasm.c
|
||||
index a0e1719..fc6c62e 100644
|
||||
index e5ae89a..7a7f8b4 100644
|
||||
--- a/asm/nasm.c
|
||||
+++ b/asm/nasm.c
|
||||
@@ -938,7 +938,8 @@ enum text_options {
|
||||
OPT_LIMIT,
|
||||
@@ -939,6 +939,7 @@ enum text_options {
|
||||
OPT_KEEP_ALL,
|
||||
OPT_NO_LINE,
|
||||
- OPT_DEBUG
|
||||
+ OPT_DEBUG,
|
||||
+ OPT_DEBUG_PREFIX_MAP
|
||||
OPT_DEBUG,
|
||||
+ OPT_DEBUG_PREFIX_MAP,
|
||||
OPT_REPRODUCIBLE
|
||||
};
|
||||
enum need_arg {
|
||||
ARG_NO,
|
||||
@@ -970,6 +971,7 @@ static const struct textargs textopts[] = {
|
||||
@@ -971,6 +972,7 @@ static const struct textargs textopts[] = {
|
||||
{"keep-all", OPT_KEEP_ALL, ARG_NO, 0},
|
||||
{"no-line", OPT_NO_LINE, ARG_NO, 0},
|
||||
{"debug", OPT_DEBUG, ARG_MAYBE, 0},
|
||||
+ {"debug-prefix-map", OPT_DEBUG_PREFIX_MAP, true, 0},
|
||||
{"reproducible", OPT_REPRODUCIBLE, ARG_NO, 0},
|
||||
{NULL, OPT_BOGUS, ARG_NO, 0}
|
||||
};
|
||||
|
||||
@@ -1332,6 +1334,26 @@ static bool process_arg(char *p, char *q, int pass)
|
||||
case OPT_DEBUG:
|
||||
debug_nasm = param ? strtoul(param, NULL, 10) : debug_nasm+1;
|
||||
@@ -1337,6 +1339,26 @@ static bool process_arg(char *p, char *q, int pass)
|
||||
case OPT_REPRODUCIBLE:
|
||||
reproducible = true;
|
||||
break;
|
||||
+ case OPT_DEBUG_PREFIX_MAP: {
|
||||
+ struct debug_prefix_list *d;
|
||||
@@ -75,7 +73,7 @@ index a0e1719..fc6c62e 100644
|
||||
case OPT_HELP:
|
||||
help(stdout);
|
||||
exit(0);
|
||||
@@ -2297,6 +2319,8 @@ static void help(FILE *out)
|
||||
@@ -2304,6 +2326,8 @@ static void help(FILE *out)
|
||||
" -w-x disable warning x (also -Wno-x)\n"
|
||||
" -w[+-]error promote all warnings to errors (also -Werror)\n"
|
||||
" -w[+-]error=x promote warning x to errors (also -Werror=x)\n"
|
||||
@@ -85,7 +83,7 @@ index a0e1719..fc6c62e 100644
|
||||
|
||||
fprintf(out, " %-20s %s\n",
|
||||
diff --git a/include/nasmlib.h b/include/nasmlib.h
|
||||
index e9bfbcc..98fc653 100644
|
||||
index 438178d..4c3e90d 100644
|
||||
--- a/include/nasmlib.h
|
||||
+++ b/include/nasmlib.h
|
||||
@@ -250,10 +250,19 @@ int64_t readstrnum(char *str, int length, bool *warn);
|
||||
@@ -181,10 +179,10 @@ index 54b22f8..c4a412c 100644
|
||||
|
||||
static void as86_cleanup(void)
|
||||
diff --git a/output/outcoff.c b/output/outcoff.c
|
||||
index bcd9ff3..15bfcf3 100644
|
||||
index 58fa024..14baf7b 100644
|
||||
--- a/output/outcoff.c
|
||||
+++ b/output/outcoff.c
|
||||
@@ -1095,14 +1095,14 @@ static void coff_symbol(char *name, int32_t strpos, int32_t value,
|
||||
@@ -1072,14 +1072,14 @@ static void coff_symbol(char *name, int32_t strpos, int32_t value,
|
||||
|
||||
static void coff_write_symbols(void)
|
||||
{
|
||||
@@ -215,7 +213,7 @@ index 61af020..1292958 100644
|
||||
nsects = sectlen = 0;
|
||||
syms = saa_init((int32_t)sizeof(struct elf_symbol));
|
||||
diff --git a/output/outieee.c b/output/outieee.c
|
||||
index 4cc0f0f..2468724 100644
|
||||
index 6d6d4b2..cdb8333 100644
|
||||
--- a/output/outieee.c
|
||||
+++ b/output/outieee.c
|
||||
@@ -207,7 +207,7 @@ static void ieee_unqualified_name(char *, char *);
|
||||
@@ -228,10 +226,10 @@ index 4cc0f0f..2468724 100644
|
||||
fpubhead = NULL;
|
||||
fpubtail = &fpubhead;
|
||||
diff --git a/output/outobj.c b/output/outobj.c
|
||||
index 0d4d311..d8dd6a0 100644
|
||||
index 56b43f9..fefea94 100644
|
||||
--- a/output/outobj.c
|
||||
+++ b/output/outobj.c
|
||||
@@ -638,7 +638,7 @@ static enum directive_result obj_directive(enum directive, char *);
|
||||
@@ -644,7 +644,7 @@ static enum directive_result obj_directive(enum directive, char *);
|
||||
|
||||
static void obj_init(void)
|
||||
{
|
||||
|
||||
104
meta/recipes-devtools/nasm/nasm/CVE-2022-44370.patch
Normal file
104
meta/recipes-devtools/nasm/nasm/CVE-2022-44370.patch
Normal file
@@ -0,0 +1,104 @@
|
||||
From b37677f7e40276bd8f504584bcba2c092f1146a8 Mon Sep 17 00:00:00 2001
|
||||
From: "H. Peter Anvin" <hpa@zytor.com>
|
||||
Date: Mon, 7 Nov 2022 10:26:03 -0800
|
||||
Subject: [PATCH] quote_for_pmake: fix counter underrun resulting in segfault
|
||||
|
||||
while (nbs--) { ... } ends with nbs == -1. Rather than a minimal fix,
|
||||
introduce mempset() to make these kinds of errors less likely in the
|
||||
future.
|
||||
|
||||
Fixes: https://bugzilla.nasm.us/show_bug.cgi?id=3392815
|
||||
Reported-by: <13579and24680@gmail.com>
|
||||
Signed-off-by: H. Peter Anvin <hpa@zytor.com>
|
||||
|
||||
Upstream-Status: Backport
|
||||
CVE: CVE-2022-4437
|
||||
|
||||
Reference to upstream patch:
|
||||
[https://github.com/netwide-assembler/nasm/commit/2d4e6952417ec6f08b6f135d2b5d0e19b7dae30d]
|
||||
|
||||
Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
|
||||
---
|
||||
asm/nasm.c | 12 +++++-------
|
||||
configure.ac | 1 +
|
||||
include/compiler.h | 7 +++++++
|
||||
3 files changed, 13 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/asm/nasm.c b/asm/nasm.c
|
||||
index 7a7f8b4..675cff4 100644
|
||||
--- a/asm/nasm.c
|
||||
+++ b/asm/nasm.c
|
||||
@@ -1,6 +1,6 @@
|
||||
/* ----------------------------------------------------------------------- *
|
||||
*
|
||||
- * Copyright 1996-2020 The NASM Authors - All Rights Reserved
|
||||
+ * Copyright 1996-2022 The NASM Authors - All Rights Reserved
|
||||
* See the file AUTHORS included with the NASM distribution for
|
||||
* the specific copyright holders.
|
||||
*
|
||||
@@ -814,8 +814,7 @@ static char *quote_for_pmake(const char *str)
|
||||
}
|
||||
|
||||
/* Convert N backslashes at the end of filename to 2N backslashes */
|
||||
- if (nbs)
|
||||
- n += nbs;
|
||||
+ n += nbs;
|
||||
|
||||
os = q = nasm_malloc(n);
|
||||
|
||||
@@ -824,10 +823,10 @@ static char *quote_for_pmake(const char *str)
|
||||
switch (*p) {
|
||||
case ' ':
|
||||
case '\t':
|
||||
- while (nbs--)
|
||||
- *q++ = '\\';
|
||||
+ q = mempset(q, '\\', nbs);
|
||||
*q++ = '\\';
|
||||
*q++ = *p;
|
||||
+ nbs = 0;
|
||||
break;
|
||||
case '$':
|
||||
*q++ = *p;
|
||||
@@ -849,9 +848,8 @@ static char *quote_for_pmake(const char *str)
|
||||
break;
|
||||
}
|
||||
}
|
||||
- while (nbs--)
|
||||
- *q++ = '\\';
|
||||
|
||||
+ q = mempset(q, '\\', nbs);
|
||||
*q = '\0';
|
||||
|
||||
return os;
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index 39680b1..940ebe2 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -199,6 +199,7 @@ AC_CHECK_FUNCS(strrchrnul)
|
||||
AC_CHECK_FUNCS(iscntrl)
|
||||
AC_CHECK_FUNCS(isascii)
|
||||
AC_CHECK_FUNCS(mempcpy)
|
||||
+AC_CHECK_FUNCS(mempset)
|
||||
|
||||
AC_CHECK_FUNCS(getuid)
|
||||
AC_CHECK_FUNCS(getgid)
|
||||
diff --git a/include/compiler.h b/include/compiler.h
|
||||
index db3d6d6..b64da6a 100644
|
||||
--- a/include/compiler.h
|
||||
+++ b/include/compiler.h
|
||||
@@ -256,6 +256,13 @@ static inline void *mempcpy(void *dst, const void *src, size_t n)
|
||||
}
|
||||
#endif
|
||||
|
||||
+#ifndef HAVE_MEMPSET
|
||||
+static inline void *mempset(void *dst, int c, size_t n)
|
||||
+{
|
||||
+ return (char *)memset(dst, c, n) + n;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
/*
|
||||
* Hack to support external-linkage inline functions
|
||||
*/
|
||||
--
|
||||
2.40.0
|
||||
@@ -8,13 +8,14 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=90904486f8fbf1861cf42752e1a39efe"
|
||||
SRC_URI = "http://www.nasm.us/pub/nasm/releasebuilds/${PV}/nasm-${PV}.tar.bz2 \
|
||||
file://0001-stdlib-Add-strlcat.patch \
|
||||
file://0002-Add-debug-prefix-map-option.patch \
|
||||
file://CVE-2022-44370.patch \
|
||||
"
|
||||
|
||||
SRC_URI[sha256sum] = "04e7343d9bf112bffa9fda86f6c7c8b120c2ccd700b882e2db9f57484b1bd778"
|
||||
SRC_URI[sha256sum] = "3c4b8339e5ab54b1bcb2316101f8985a5da50a3f9e504d43fa6f35668bee2fd0"
|
||||
|
||||
EXTRA_AUTORECONF_append = " -I autoconf/m4"
|
||||
|
||||
inherit autotools
|
||||
inherit autotools-brokensep
|
||||
|
||||
BBCLASSEXTEND = "native"
|
||||
|
||||
@@ -8,6 +8,8 @@ PYPI_PACKAGE_EXT = "zip"
|
||||
|
||||
inherit pypi
|
||||
|
||||
SRC_URI += " file://CVE-2022-40897.patch "
|
||||
|
||||
SRC_URI_append_class-native = " file://0001-conditionally-do-not-fetch-code-by-easy_install.patch"
|
||||
|
||||
SRC_URI[md5sum] = "0c956eea142af9c2b02d72e3c042af30"
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
From 43a9c9bfa6aa626ec2a22540bea28d2ca77964be Mon Sep 17 00:00:00 2001
|
||||
From: "Jason R. Coombs" <jaraco@jaraco.com>
|
||||
Date: Fri, 4 Nov 2022 13:47:53 -0400
|
||||
Subject: [PATCH] Limit the amount of whitespace to search/backtrack. Fixes
|
||||
#3659.
|
||||
|
||||
CVE: CVE-2022-40897
|
||||
Upstream-Status: Backport [
|
||||
Upstream : https://github.com/pypa/setuptools/commit/43a9c9bfa6aa626ec2a22540bea28d2ca77964be
|
||||
Import from Ubuntu: http://archive.ubuntu.com/ubuntu/pool/main/s/setuptools/setuptools_45.2.0-1ubuntu0.1.debian.tar.xz
|
||||
]
|
||||
Signed-off-by: Lee Chee Yang <chee.yang.lee@intel.com>
|
||||
|
||||
---
|
||||
setuptools/package_index.py | 2 +-
|
||||
setuptools/tests/test_packageindex.py | 1 -
|
||||
2 files changed, 1 insertion(+), 2 deletions(-)
|
||||
|
||||
--- setuptools-45.2.0.orig/setuptools/package_index.py
|
||||
+++ setuptools-45.2.0/setuptools/package_index.py
|
||||
@@ -215,7 +215,7 @@ def unique_values(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
-REL = re.compile(r"""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
|
||||
+REL = re.compile(r"""<([^>]*\srel\s{0,10}=\s{0,10}['"]?([^'" >]+)[^>]*)>""", re.I)
|
||||
# this line is here to fix emacs' cruddy broken syntax highlighting
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ SRC_URI_append_class-native = " \
|
||||
file://0001-Don-t-search-system-for-headers-libraries.patch \
|
||||
"
|
||||
|
||||
SRC_URI[md5sum] = "70223497e664524303ca2364208647e1"
|
||||
SRC_URI[sha256sum] = "2e54b0c68191f16552f6de2e97a2396540572a219f6bbb28591a137cecc490a9"
|
||||
SRC_URI[md5sum] = "5ea6267ea00513fc31d3746feb35842d"
|
||||
SRC_URI[sha256sum] = "3ffb71cd349a326ba7b2fadc7e7df86ba577dd9c4917e52a8401adbda7405e3f"
|
||||
|
||||
# exclude pre-releases for both python 2.x and 3.x
|
||||
UPSTREAM_CHECK_REGEX = "[Pp]ython-(?P<pver>\d+(\.\d+)+).tar"
|
||||
@@ -137,10 +137,10 @@ SRC_URI = "https://download.qemu.org/${BPN}-${PV}.tar.xz \
|
||||
file://CVE-2021-3409-4.patch \
|
||||
file://CVE-2021-3409-5.patch \
|
||||
file://hw-display-qxl-Pass-requested-buffer-size-to-qxl_phy.patch \
|
||||
file://CVE-2023-0330_1.patch \
|
||||
file://CVE-2023-0330_2.patch \
|
||||
file://CVE-2023-0330.patch \
|
||||
file://CVE-2023-3354.patch \
|
||||
file://CVE-2023-3180.patch \
|
||||
file://CVE-2020-24165.patch \
|
||||
"
|
||||
UPSTREAM_CHECK_REGEX = "qemu-(?P<pver>\d+(\.\d+)+)\.tar"
|
||||
|
||||
@@ -166,6 +166,13 @@ CVE_CHECK_WHITELIST += "CVE-2020-27661"
|
||||
# this bug related to windows specific.
|
||||
CVE_CHECK_WHITELIST += "CVE-2023-0664"
|
||||
|
||||
# As per https://bugzilla.redhat.com/show_bug.cgi?id=2203387
|
||||
# RHEL specific issue
|
||||
CVE_CHECK_WHITELIST += "CVE-2023-2680"
|
||||
|
||||
# Affected only `qemu-kvm` shipped with Red Hat Enterprise Linux 8.3 release.
|
||||
CVE_CHECK_WHITELIST += "CVE-2021-20295"
|
||||
|
||||
COMPATIBLE_HOST_mipsarchn32 = "null"
|
||||
COMPATIBLE_HOST_mipsarchn64 = "null"
|
||||
|
||||
|
||||
94
meta/recipes-devtools/qemu/qemu/CVE-2020-24165.patch
Normal file
94
meta/recipes-devtools/qemu/qemu/CVE-2020-24165.patch
Normal file
@@ -0,0 +1,94 @@
|
||||
CVE: CVE-2020-24165
|
||||
Upstream-Status: Backport [https://github.com/qemu/qemu/commit/886cc68943ebe8cf7e5f970be33459f95068a441 ]
|
||||
Signed-off-by: Lee Chee Yang <chee.yang.lee@intel.com>
|
||||
|
||||
From 886cc68943ebe8cf7e5f970be33459f95068a441 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Alex=20Benn=C3=A9e?= <alex.bennee@linaro.org>
|
||||
Date: Fri, 14 Feb 2020 14:49:52 +0000
|
||||
Subject: [PATCH] accel/tcg: fix race in cpu_exec_step_atomic (bug 1863025)
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The bug describes a race whereby cpu_exec_step_atomic can acquire a TB
|
||||
which is invalidated by a tb_flush before we execute it. This doesn't
|
||||
affect the other cpu_exec modes as a tb_flush by it's nature can only
|
||||
occur on a quiescent system. The race was described as:
|
||||
|
||||
B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
|
||||
B3. tcg_tb_alloc obtains a new TB
|
||||
|
||||
C3. TB obtained with tb_lookup__cpu_state or tb_gen_code
|
||||
(same TB as B2)
|
||||
|
||||
A3. start_exclusive critical section entered
|
||||
A4. do_tb_flush is called, TB memory freed/re-allocated
|
||||
A5. end_exclusive exits critical section
|
||||
|
||||
B2. tcg_cpu_exec => cpu_exec => tb_find => tb_gen_code
|
||||
B3. tcg_tb_alloc reallocates TB from B2
|
||||
|
||||
C4. start_exclusive critical section entered
|
||||
C5. cpu_tb_exec executes the TB code that was free in A4
|
||||
|
||||
The simplest fix is to widen the exclusive period to include the TB
|
||||
lookup. As a result we can drop the complication of checking we are in
|
||||
the exclusive region before we end it.
|
||||
|
||||
Cc: Yifan <me@yifanlu.com>
|
||||
Buglink: https://bugs.launchpad.net/qemu/+bug/1863025
|
||||
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
|
||||
Signed-off-by: Alex Bennée <alex.bennee@linaro.org>
|
||||
Message-Id: <20200214144952.15502-1-alex.bennee@linaro.org>
|
||||
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
|
||||
---
|
||||
accel/tcg/cpu-exec.c | 21 +++++++++++----------
|
||||
1 file changed, 11 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
|
||||
index 2560c90eec79..d95c4848a47b 100644
|
||||
--- a/accel/tcg/cpu-exec.c
|
||||
+++ b/accel/tcg/cpu-exec.c
|
||||
@@ -240,6 +240,8 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
uint32_t cf_mask = cflags & CF_HASH_MASK;
|
||||
|
||||
if (sigsetjmp(cpu->jmp_env, 0) == 0) {
|
||||
+ start_exclusive();
|
||||
+
|
||||
tb = tb_lookup__cpu_state(cpu, &pc, &cs_base, &flags, cf_mask);
|
||||
if (tb == NULL) {
|
||||
mmap_lock();
|
||||
@@ -247,8 +249,6 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
mmap_unlock();
|
||||
}
|
||||
|
||||
- start_exclusive();
|
||||
-
|
||||
/* Since we got here, we know that parallel_cpus must be true. */
|
||||
parallel_cpus = false;
|
||||
cc->cpu_exec_enter(cpu);
|
||||
@@ -271,14 +271,15 @@ void cpu_exec_step_atomic(CPUState *cpu)
|
||||
qemu_plugin_disable_mem_helpers(cpu);
|
||||
}
|
||||
|
||||
- if (cpu_in_exclusive_context(cpu)) {
|
||||
- /* We might longjump out of either the codegen or the
|
||||
- * execution, so must make sure we only end the exclusive
|
||||
- * region if we started it.
|
||||
- */
|
||||
- parallel_cpus = true;
|
||||
- end_exclusive();
|
||||
- }
|
||||
+
|
||||
+ /*
|
||||
+ * As we start the exclusive region before codegen we must still
|
||||
+ * be in the region if we longjump out of either the codegen or
|
||||
+ * the execution.
|
||||
+ */
|
||||
+ g_assert(cpu_in_exclusive_context(cpu));
|
||||
+ parallel_cpus = true;
|
||||
+ end_exclusive();
|
||||
}
|
||||
|
||||
struct tb_desc {
|
||||
@@ -1,135 +0,0 @@
|
||||
From a2e1753b8054344f32cf94f31c6399a58794a380 Mon Sep 17 00:00:00 2001
|
||||
From: Alexander Bulekov <alxndr@bu.edu>
|
||||
Date: Thu, 27 Apr 2023 17:10:06 -0400
|
||||
Subject: [PATCH] memory: prevent dma-reentracy issues
|
||||
|
||||
Add a flag to the DeviceState, when a device is engaged in PIO/MMIO/DMA.
|
||||
This flag is set/checked prior to calling a device's MemoryRegion
|
||||
handlers, and set when device code initiates DMA. The purpose of this
|
||||
flag is to prevent two types of DMA-based reentrancy issues:
|
||||
|
||||
1.) mmio -> dma -> mmio case
|
||||
2.) bh -> dma write -> mmio case
|
||||
|
||||
These issues have led to problems such as stack-exhaustion and
|
||||
use-after-frees.
|
||||
|
||||
Summary of the problem from Peter Maydell:
|
||||
https://lore.kernel.org/qemu-devel/CAFEAcA_23vc7hE3iaM-JVA6W38LK4hJoWae5KcknhPRD5fPBZA@mail.gmail.com
|
||||
|
||||
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/62
|
||||
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/540
|
||||
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/541
|
||||
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/556
|
||||
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/557
|
||||
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/827
|
||||
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1282
|
||||
Resolves: CVE-2023-0330
|
||||
|
||||
Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
|
||||
Reviewed-by: Thomas Huth <thuth@redhat.com>
|
||||
Message-Id: <20230427211013.2994127-2-alxndr@bu.edu>
|
||||
[thuth: Replace warn_report() with warn_report_once()]
|
||||
Signed-off-by: Thomas Huth <thuth@redhat.com>
|
||||
|
||||
Upstream-Status: Backport [https://gitlab.com/qemu-project/qemu/-/commit/a2e1753b8054344f32cf94f31c6399a58794a380]
|
||||
CVE: CVE-2023-0330
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
include/exec/memory.h | 5 +++++
|
||||
include/hw/qdev-core.h | 7 +++++++
|
||||
memory.c | 16 ++++++++++++++++
|
||||
3 files changed, 28 insertions(+)
|
||||
|
||||
diff --git a/include/exec/memory.h b/include/exec/memory.h
|
||||
index 2b8bccdd..0c8cdb8e 100644
|
||||
--- a/include/exec/memory.h
|
||||
+++ b/include/exec/memory.h
|
||||
@@ -378,6 +378,8 @@ struct MemoryRegion {
|
||||
bool is_iommu;
|
||||
RAMBlock *ram_block;
|
||||
Object *owner;
|
||||
+ /* owner as TYPE_DEVICE. Used for re-entrancy checks in MR access hotpath */
|
||||
+ DeviceState *dev;
|
||||
|
||||
const MemoryRegionOps *ops;
|
||||
void *opaque;
|
||||
@@ -400,6 +402,9 @@ struct MemoryRegion {
|
||||
const char *name;
|
||||
unsigned ioeventfd_nb;
|
||||
MemoryRegionIoeventfd *ioeventfds;
|
||||
+
|
||||
+ /* For devices designed to perform re-entrant IO into their own IO MRs */
|
||||
+ bool disable_reentrancy_guard;
|
||||
};
|
||||
|
||||
struct IOMMUMemoryRegion {
|
||||
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
|
||||
index 1518495b..206f0a70 100644
|
||||
--- a/include/hw/qdev-core.h
|
||||
+++ b/include/hw/qdev-core.h
|
||||
@@ -138,6 +138,10 @@ struct NamedGPIOList {
|
||||
QLIST_ENTRY(NamedGPIOList) node;
|
||||
};
|
||||
|
||||
+typedef struct {
|
||||
+ bool engaged_in_io;
|
||||
+} MemReentrancyGuard;
|
||||
+
|
||||
/**
|
||||
* DeviceState:
|
||||
* @realized: Indicates whether the device has been fully constructed.
|
||||
@@ -163,6 +167,9 @@ struct DeviceState {
|
||||
int num_child_bus;
|
||||
int instance_id_alias;
|
||||
int alias_required_for_version;
|
||||
+
|
||||
+ /* Is the device currently in mmio/pio/dma? Used to prevent re-entrancy */
|
||||
+ MemReentrancyGuard mem_reentrancy_guard;
|
||||
};
|
||||
|
||||
struct DeviceListener {
|
||||
diff --git a/memory.c b/memory.c
|
||||
index 8cafb86a..94ebcaf9 100644
|
||||
--- a/memory.c
|
||||
+++ b/memory.c
|
||||
@@ -531,6 +531,18 @@ static MemTxResult access_with_adjusted_size(hwaddr addr,
|
||||
access_size_max = 4;
|
||||
}
|
||||
|
||||
+ /* Do not allow more than one simultaneous access to a device's IO Regions */
|
||||
+ if (mr->dev && !mr->disable_reentrancy_guard &&
|
||||
+ !mr->ram_device && !mr->ram && !mr->rom_device && !mr->readonly) {
|
||||
+ if (mr->dev->mem_reentrancy_guard.engaged_in_io) {
|
||||
+ warn_report_once("Blocked re-entrant IO on MemoryRegion: "
|
||||
+ "%s at addr: 0x%" HWADDR_PRIX,
|
||||
+ memory_region_name(mr), addr);
|
||||
+ return MEMTX_ACCESS_ERROR;
|
||||
+ }
|
||||
+ mr->dev->mem_reentrancy_guard.engaged_in_io = true;
|
||||
+ }
|
||||
+
|
||||
/* FIXME: support unaligned access? */
|
||||
access_size = MAX(MIN(size, access_size_max), access_size_min);
|
||||
access_mask = MAKE_64BIT_MASK(0, access_size * 8);
|
||||
@@ -545,6 +557,9 @@ static MemTxResult access_with_adjusted_size(hwaddr addr,
|
||||
access_mask, attrs);
|
||||
}
|
||||
}
|
||||
+ if (mr->dev) {
|
||||
+ mr->dev->mem_reentrancy_guard.engaged_in_io = false;
|
||||
+ }
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -1132,6 +1147,7 @@ static void memory_region_do_init(MemoryRegion *mr,
|
||||
}
|
||||
mr->name = g_strdup(name);
|
||||
mr->owner = owner;
|
||||
+ mr->dev = (DeviceState *) object_dynamic_cast(mr->owner, TYPE_DEVICE);
|
||||
mr->ram_block = NULL;
|
||||
|
||||
if (name) {
|
||||
--
|
||||
2.25.1
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
From 77ff5f1be394eb2c786df561ff37dde7f982ec76 Mon Sep 17 00:00:00 2001
|
||||
From: Stefano Babic <sbabic@denx.de>
|
||||
Date: Fri, 28 Jul 2017 13:20:52 +0200
|
||||
Subject: [PATCH] Wrong CRC with ASCII CRC for large files
|
||||
|
||||
Due to signedness, the checksum is not computed when filesize is bigger
|
||||
a 2GB.
|
||||
|
||||
Upstream-Status: Submitted [https://lists.gnu.org/archive/html/bug-cpio/2017-07/msg00004.html]
|
||||
Signed-off-by: Stefano Babic <sbabic@denx.de>
|
||||
---
|
||||
src/copyout.c | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/copyout.c b/src/copyout.c
|
||||
index 1f0987a..727aeca 100644
|
||||
--- a/src/copyout.c
|
||||
+++ b/src/copyout.c
|
||||
@@ -34,13 +34,13 @@
|
||||
compute and return a checksum for them. */
|
||||
|
||||
static uint32_t
|
||||
-read_for_checksum (int in_file_des, int file_size, char *file_name)
|
||||
+read_for_checksum (int in_file_des, unsigned int file_size, char *file_name)
|
||||
{
|
||||
uint32_t crc;
|
||||
char buf[BUFSIZ];
|
||||
- int bytes_left;
|
||||
- int bytes_read;
|
||||
- int i;
|
||||
+ unsigned int bytes_left;
|
||||
+ unsigned int bytes_read;
|
||||
+ unsigned int i;
|
||||
|
||||
crc = 0;
|
||||
|
||||
--
|
||||
2.7.4
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
From d257e47a6c6b41ba727b196ac96c05ab91bd9d65 Mon Sep 17 00:00:00 2001
|
||||
From: Sergey Poznyakoff <gray@gnu.org>
|
||||
Date: Fri, 7 Apr 2023 11:23:37 +0300
|
||||
Subject: [PATCH 3/4] Fix calculation of CRC in copy-out mode.
|
||||
|
||||
* src/copyout.c (read_for_checksum): Fix type of the file_size argument.
|
||||
Rewrite the reading loop.
|
||||
|
||||
Original patch by Stefano Babic <sbabic@denx.de>
|
||||
|
||||
Upstream-Status: Backport [a1b2f7871c3ae5113e0102b870b15ea06a8f0e3d]
|
||||
Signed-off-by: Marek Vasut <marex@denx.de>
|
||||
---
|
||||
src/copyout.c | 16 +++++++---------
|
||||
1 file changed, 7 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/src/copyout.c b/src/copyout.c
|
||||
index 8b0beb6..f1ff351 100644
|
||||
--- a/src/copyout.c
|
||||
+++ b/src/copyout.c
|
||||
@@ -34,27 +34,25 @@
|
||||
compute and return a checksum for them. */
|
||||
|
||||
static uint32_t
|
||||
-read_for_checksum (int in_file_des, int file_size, char *file_name)
|
||||
+read_for_checksum (int in_file_des, off_t file_size, char *file_name)
|
||||
{
|
||||
uint32_t crc;
|
||||
- char buf[BUFSIZ];
|
||||
- int bytes_left;
|
||||
- int bytes_read;
|
||||
- int i;
|
||||
+ unsigned char buf[BUFSIZ];
|
||||
+ ssize_t bytes_read;
|
||||
+ ssize_t i;
|
||||
|
||||
crc = 0;
|
||||
|
||||
- for (bytes_left = file_size; bytes_left > 0; bytes_left -= bytes_read)
|
||||
+ while (file_size > 0)
|
||||
{
|
||||
bytes_read = read (in_file_des, buf, BUFSIZ);
|
||||
if (bytes_read < 0)
|
||||
error (PAXEXIT_FAILURE, errno, _("cannot read checksum for %s"), file_name);
|
||||
if (bytes_read == 0)
|
||||
break;
|
||||
- if (bytes_left < bytes_read)
|
||||
- bytes_read = bytes_left;
|
||||
- for (i = 0; i < bytes_read; ++i)
|
||||
+ for (i = 0; i < bytes_read; i++)
|
||||
crc += buf[i] & 0xff;
|
||||
+ file_size -= bytes_read;
|
||||
}
|
||||
if (lseek (in_file_des, 0L, SEEK_SET))
|
||||
error (PAXEXIT_FAILURE, errno, _("cannot read checksum for %s"), file_name);
|
||||
--
|
||||
2.39.2
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
From 8513495ab5cfb63eb7c4c933fdf0b78c6196cd27 Mon Sep 17 00:00:00 2001
|
||||
From: Sergey Poznyakoff <gray@gnu.org>
|
||||
Date: Fri, 28 Apr 2023 15:23:46 +0300
|
||||
Subject: [PATCH 4/4] Fix appending to archives bigger than 2G
|
||||
|
||||
* src/extern.h (last_header_start): Change type to off_t.
|
||||
* src/global.c: Likewise.
|
||||
* src/util.c (prepare_append): Use off_t for file offsets.
|
||||
|
||||
Upstream-Status: Backport [0987d63384f0419b4b14aecdc6a61729b75ce86a]
|
||||
Signed-off-by: Marek Vasut <marex@denx.de>
|
||||
---
|
||||
src/extern.h | 11 ++++-----
|
||||
src/global.c | 2 +-
|
||||
src/util.c | 66 ++++++++++++++++++++++++++--------------------------
|
||||
3 files changed, 39 insertions(+), 40 deletions(-)
|
||||
|
||||
diff --git a/src/extern.h b/src/extern.h
|
||||
index 11ac6bf..12f14a9 100644
|
||||
--- a/src/extern.h
|
||||
+++ b/src/extern.h
|
||||
@@ -67,7 +67,7 @@ extern int ignore_devno_option;
|
||||
|
||||
extern bool to_stdout_option;
|
||||
|
||||
-extern int last_header_start;
|
||||
+extern off_t last_header_start;
|
||||
extern int copy_matching_files;
|
||||
extern int numeric_uid;
|
||||
extern char *pattern_file_name;
|
||||
@@ -123,7 +123,7 @@ void field_width_error (const char *filename, const char *fieldname,
|
||||
|
||||
/* copypass.c */
|
||||
void process_copy_pass (void);
|
||||
-int link_to_maj_min_ino (char *file_name, int st_dev_maj,
|
||||
+int link_to_maj_min_ino (char *file_name, int st_dev_maj,
|
||||
int st_dev_min, ino_t st_ino);
|
||||
int link_to_name (char const *link_name, char const *link_target);
|
||||
|
||||
@@ -171,7 +171,7 @@ void copy_files_tape_to_disk (int in_des, int out_des, off_t num_bytes);
|
||||
void copy_files_disk_to_tape (int in_des, int out_des, off_t num_bytes, char *filename);
|
||||
void copy_files_disk_to_disk (int in_des, int out_des, off_t num_bytes, char *filename);
|
||||
void warn_if_file_changed (char *file_name, off_t old_file_size,
|
||||
- time_t old_file_mtime);
|
||||
+ time_t old_file_mtime);
|
||||
void create_all_directories (char const *name);
|
||||
void prepare_append (int out_file_des);
|
||||
char *find_inode_file (ino_t node_num,
|
||||
@@ -185,7 +185,7 @@ void set_new_media_message (char *message);
|
||||
#ifdef HPUX_CDF
|
||||
char *add_cdf_double_slashes (char *filename);
|
||||
#endif
|
||||
-void write_nuls_to_file (off_t num_bytes, int out_des,
|
||||
+void write_nuls_to_file (off_t num_bytes, int out_des,
|
||||
void (*writer) (char *in_buf,
|
||||
int out_des, off_t num_bytes));
|
||||
#define DISK_IO_BLOCK_SIZE 512
|
||||
@@ -229,6 +229,5 @@ void delay_set_stat (char const *file_name, struct stat *st,
|
||||
mode_t invert_permissions);
|
||||
int repair_delayed_set_stat (struct cpio_file_stat *file_hdr);
|
||||
void apply_delayed_set_stat (void);
|
||||
-
|
||||
-int arf_stores_inode_p (enum archive_format arf);
|
||||
|
||||
+int arf_stores_inode_p (enum archive_format arf);
|
||||
diff --git a/src/global.c b/src/global.c
|
||||
index fb3abe9..5c9fc05 100644
|
||||
--- a/src/global.c
|
||||
+++ b/src/global.c
|
||||
@@ -114,7 +114,7 @@ int debug_flag = false;
|
||||
|
||||
/* File position of last header read. Only used during -A to determine
|
||||
where the old TRAILER!!! record started. */
|
||||
-int last_header_start = 0;
|
||||
+off_t last_header_start = 0;
|
||||
|
||||
/* With -i; if true, copy only files that match any of the given patterns;
|
||||
if false, copy only files that do not match any of the patterns. (-f) */
|
||||
diff --git a/src/util.c b/src/util.c
|
||||
index 4421b20..3be89a4 100644
|
||||
--- a/src/util.c
|
||||
+++ b/src/util.c
|
||||
@@ -60,8 +60,8 @@ tape_empty_output_buffer (int out_des)
|
||||
static long output_bytes_before_lseek = 0;
|
||||
|
||||
/* Some tape drivers seem to have a signed internal seek pointer and
|
||||
- they lose if it overflows and becomes negative (e.g. when writing
|
||||
- tapes > 2Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the
|
||||
+ they lose if it overflows and becomes negative (e.g. when writing
|
||||
+ tapes > 2Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the
|
||||
seek pointer and prevent it from overflowing. */
|
||||
if (output_is_special
|
||||
&& ( (output_bytes_before_lseek += output_size) >= 1073741824L) )
|
||||
@@ -106,7 +106,7 @@ static ssize_t sparse_write (int fildes, char *buf, size_t nbyte, bool flush);
|
||||
descriptor OUT_DES and reset `output_size' and `out_buff'.
|
||||
If `swapping_halfwords' or `swapping_bytes' is set,
|
||||
do the appropriate swapping first. Our callers have
|
||||
- to make sure to only set these flags if `output_size'
|
||||
+ to make sure to only set these flags if `output_size'
|
||||
is appropriate (a multiple of 4 for `swapping_halfwords',
|
||||
2 for `swapping_bytes'). The fact that DISK_IO_BLOCK_SIZE
|
||||
must always be a multiple of 4 helps us (and our callers)
|
||||
@@ -188,8 +188,8 @@ tape_fill_input_buffer (int in_des, int num_bytes)
|
||||
{
|
||||
#ifdef BROKEN_LONG_TAPE_DRIVER
|
||||
/* Some tape drivers seem to have a signed internal seek pointer and
|
||||
- they lose if it overflows and becomes negative (e.g. when writing
|
||||
- tapes > 4Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the
|
||||
+ they lose if it overflows and becomes negative (e.g. when writing
|
||||
+ tapes > 4Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the
|
||||
seek pointer and prevent it from overflowing. */
|
||||
if (input_is_special
|
||||
&& ( (input_bytes_before_lseek += num_bytes) >= 1073741824L) )
|
||||
@@ -332,8 +332,8 @@ tape_buffered_peek (char *peek_buf, int in_des, int num_bytes)
|
||||
|
||||
#ifdef BROKEN_LONG_TAPE_DRIVER
|
||||
/* Some tape drivers seem to have a signed internal seek pointer and
|
||||
- they lose if it overflows and becomes negative (e.g. when writing
|
||||
- tapes > 4Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the
|
||||
+ they lose if it overflows and becomes negative (e.g. when writing
|
||||
+ tapes > 4Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the
|
||||
seek pointer and prevent it from overflowing. */
|
||||
if (input_is_special
|
||||
&& ( (input_bytes_before_lseek += num_bytes) >= 1073741824L) )
|
||||
@@ -404,7 +404,7 @@ tape_toss_input (int in_des, off_t num_bytes)
|
||||
|
||||
if (crc_i_flag && only_verify_crc_flag)
|
||||
{
|
||||
- int k;
|
||||
+ int k;
|
||||
for (k = 0; k < space_left; ++k)
|
||||
crc += in_buff[k] & 0xff;
|
||||
}
|
||||
@@ -416,14 +416,14 @@ tape_toss_input (int in_des, off_t num_bytes)
|
||||
}
|
||||
|
||||
void
|
||||
-write_nuls_to_file (off_t num_bytes, int out_des,
|
||||
- void (*writer) (char *in_buf, int out_des, off_t num_bytes))
|
||||
+write_nuls_to_file (off_t num_bytes, int out_des,
|
||||
+ void (*writer) (char *in_buf, int out_des, off_t num_bytes))
|
||||
{
|
||||
off_t blocks;
|
||||
off_t extra_bytes;
|
||||
off_t i;
|
||||
static char zeros_512[512];
|
||||
-
|
||||
+
|
||||
blocks = num_bytes / sizeof zeros_512;
|
||||
extra_bytes = num_bytes % sizeof zeros_512;
|
||||
for (i = 0; i < blocks; ++i)
|
||||
@@ -603,7 +603,7 @@ create_all_directories (char const *name)
|
||||
char *dir;
|
||||
|
||||
dir = dir_name (name);
|
||||
-
|
||||
+
|
||||
if (dir == NULL)
|
||||
error (PAXEXIT_FAILURE, 0, _("virtual memory exhausted"));
|
||||
|
||||
@@ -637,9 +637,9 @@ create_all_directories (char const *name)
|
||||
void
|
||||
prepare_append (int out_file_des)
|
||||
{
|
||||
- int start_of_header;
|
||||
- int start_of_block;
|
||||
- int useful_bytes_in_block;
|
||||
+ off_t start_of_header;
|
||||
+ off_t start_of_block;
|
||||
+ size_t useful_bytes_in_block;
|
||||
char *tmp_buf;
|
||||
|
||||
start_of_header = last_header_start;
|
||||
@@ -697,8 +697,8 @@ inode_val_compare (const void *val1, const void *val2)
|
||||
const struct inode_val *ival1 = val1;
|
||||
const struct inode_val *ival2 = val2;
|
||||
return ival1->inode == ival2->inode
|
||||
- && ival1->major_num == ival2->major_num
|
||||
- && ival1->minor_num == ival2->minor_num;
|
||||
+ && ival1->major_num == ival2->major_num
|
||||
+ && ival1->minor_num == ival2->minor_num;
|
||||
}
|
||||
|
||||
static struct inode_val *
|
||||
@@ -706,10 +706,10 @@ find_inode_val (ino_t node_num, unsigned long major_num,
|
||||
unsigned long minor_num)
|
||||
{
|
||||
struct inode_val sample;
|
||||
-
|
||||
+
|
||||
if (!hash_table)
|
||||
return NULL;
|
||||
-
|
||||
+
|
||||
sample.inode = node_num;
|
||||
sample.major_num = major_num;
|
||||
sample.minor_num = minor_num;
|
||||
@@ -734,7 +734,7 @@ add_inode (ino_t node_num, char *file_name, unsigned long major_num,
|
||||
{
|
||||
struct inode_val *temp;
|
||||
struct inode_val *e = NULL;
|
||||
-
|
||||
+
|
||||
/* Create new inode record. */
|
||||
temp = (struct inode_val *) xmalloc (sizeof (struct inode_val));
|
||||
temp->inode = node_num;
|
||||
@@ -1007,7 +1007,7 @@ buf_all_zeros (char *buf, int bufsize)
|
||||
|
||||
/* Write NBYTE bytes from BUF to file descriptor FILDES, trying to
|
||||
create holes instead of writing blockfuls of zeros.
|
||||
-
|
||||
+
|
||||
Return the number of bytes written (including bytes in zero
|
||||
regions) on success, -1 on error.
|
||||
|
||||
@@ -1027,7 +1027,7 @@ sparse_write (int fildes, char *buf, size_t nbytes, bool flush)
|
||||
|
||||
enum { begin, in_zeros, not_in_zeros } state =
|
||||
delayed_seek_count ? in_zeros : begin;
|
||||
-
|
||||
+
|
||||
while (nbytes)
|
||||
{
|
||||
size_t rest = nbytes;
|
||||
@@ -1042,7 +1042,7 @@ sparse_write (int fildes, char *buf, size_t nbytes, bool flush)
|
||||
if (state == not_in_zeros)
|
||||
{
|
||||
ssize_t bytes = buf - start_ptr + rest;
|
||||
-
|
||||
+
|
||||
n = write (fildes, start_ptr, bytes);
|
||||
if (n == -1)
|
||||
return -1;
|
||||
@@ -1091,8 +1091,8 @@ sparse_write (int fildes, char *buf, size_t nbytes, bool flush)
|
||||
if (n != 1)
|
||||
return n;
|
||||
delayed_seek_count = 0;
|
||||
- }
|
||||
-
|
||||
+ }
|
||||
+
|
||||
return nwritten + seek_count;
|
||||
}
|
||||
|
||||
@@ -1222,7 +1222,7 @@ set_perms (int fd, struct cpio_file_stat *header)
|
||||
if (!no_chown_flag)
|
||||
{
|
||||
uid_t uid = CPIO_UID (header->c_uid);
|
||||
- gid_t gid = CPIO_GID (header->c_gid);
|
||||
+ gid_t gid = CPIO_GID (header->c_gid);
|
||||
if ((fchown_or_chown (fd, header->c_name, uid, gid) < 0)
|
||||
&& errno != EPERM)
|
||||
chown_error_details (header->c_name, uid, gid);
|
||||
@@ -1239,13 +1239,13 @@ set_file_times (int fd,
|
||||
const char *name, unsigned long atime, unsigned long mtime)
|
||||
{
|
||||
struct timespec ts[2];
|
||||
-
|
||||
+
|
||||
memset (&ts, 0, sizeof ts);
|
||||
|
||||
ts[0].tv_sec = atime;
|
||||
ts[1].tv_sec = mtime;
|
||||
|
||||
- /* Silently ignore EROFS because reading the file won't have upset its
|
||||
+ /* Silently ignore EROFS because reading the file won't have upset its
|
||||
timestamp if it's on a read-only filesystem. */
|
||||
if (fdutimens (fd, name, ts) < 0 && errno != EROFS)
|
||||
utime_error (name);
|
||||
@@ -1297,7 +1297,7 @@ cpio_safer_name_suffix (char *name, bool link_target, bool absolute_names,
|
||||
|
||||
/* This is a simplified form of delayed set_stat used by GNU tar.
|
||||
With the time, both forms will merge and pass to paxutils
|
||||
-
|
||||
+
|
||||
List of directories whose statuses we need to extract after we've
|
||||
finished extracting their subsidiary files. If you consider each
|
||||
contiguous subsequence of elements of the form [D]?[^D]*, where [D]
|
||||
@@ -1415,7 +1415,7 @@ cpio_mkdir (struct cpio_file_stat *file_hdr, int *setstat_delayed)
|
||||
{
|
||||
int rc;
|
||||
mode_t mode = file_hdr->c_mode;
|
||||
-
|
||||
+
|
||||
if (!(file_hdr->c_mode & S_IWUSR))
|
||||
{
|
||||
rc = mkdir (file_hdr->c_name, mode | S_IWUSR);
|
||||
@@ -1438,10 +1438,10 @@ cpio_create_dir (struct cpio_file_stat *file_hdr, int existing_dir)
|
||||
{
|
||||
int res; /* Result of various function calls. */
|
||||
int setstat_delayed = 0;
|
||||
-
|
||||
+
|
||||
if (to_stdout_option)
|
||||
return 0;
|
||||
-
|
||||
+
|
||||
/* Strip any trailing `/'s off the filename; tar puts
|
||||
them on. We might as well do it here in case anybody
|
||||
else does too, since they cause strange things to happen. */
|
||||
@@ -1530,7 +1530,7 @@ arf_stores_inode_p (enum archive_format arf)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
-
|
||||
+
|
||||
void
|
||||
cpio_file_stat_init (struct cpio_file_stat *file_hdr)
|
||||
{
|
||||
--
|
||||
2.39.2
|
||||
|
||||
@@ -10,7 +10,8 @@ SRC_URI = "${GNU_MIRROR}/cpio/cpio-${PV}.tar.gz \
|
||||
file://0001-Unset-need_charset_alias-when-building-for-musl.patch \
|
||||
file://0002-src-global.c-Remove-superfluous-declaration-of-progr.patch \
|
||||
file://CVE-2021-38185.patch \
|
||||
file://0001-Wrong-CRC-with-ASCII-CRC-for-large-files.patch \
|
||||
file://0003-Fix-calculation-of-CRC-in-copy-out-mode.patch \
|
||||
file://0004-Fix-appending-to-archives-bigger-than-2G.patch \
|
||||
"
|
||||
|
||||
SRC_URI[md5sum] = "389c5452d667c23b5eceb206f5000810"
|
||||
|
||||
@@ -16,6 +16,8 @@ SRC_URI = "https://github.com/apple/cups/releases/download/v${PV}/${BP}-source.t
|
||||
file://CVE-2022-26691.patch \
|
||||
file://CVE-2023-32324.patch \
|
||||
file://CVE-2023-34241.patch \
|
||||
file://CVE-2023-32360.patch \
|
||||
file://CVE-2023-4504.patch \
|
||||
"
|
||||
|
||||
UPSTREAM_CHECK_URI = "https://github.com/apple/cups/releases"
|
||||
|
||||
31
meta/recipes-extended/cups/cups/CVE-2023-32360.patch
Normal file
31
meta/recipes-extended/cups/cups/CVE-2023-32360.patch
Normal file
@@ -0,0 +1,31 @@
|
||||
From a0c8b9c9556882f00c68b9727a95a1b6d1452913 Mon Sep 17 00:00:00 2001
|
||||
From: Michael R Sweet <michael.r.sweet@gmail.com>
|
||||
Date: Tue, 6 Dec 2022 09:04:01 -0500
|
||||
Subject: [PATCH] Require authentication for CUPS-Get-Document.
|
||||
|
||||
Upstream-Status: Backport [https://github.com/OpenPrinting/cups/commit/a0c8b9c9556882f00c68b9727a95a1b6d1452913]
|
||||
CVE: CVE-2023-32360
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
conf/cupsd.conf.in | 8 +++++++-
|
||||
1 file changed, 7 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/conf/cupsd.conf.in b/conf/cupsd.conf.in
|
||||
index b258849078..a07536f3e4 100644
|
||||
--- a/conf/cupsd.conf.in
|
||||
+++ b/conf/cupsd.conf.in
|
||||
@@ -68,7 +68,13 @@ IdleExitTimeout @EXIT_TIMEOUT@
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
|
||||
- <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job CUPS-Get-Document>
|
||||
+ <Limit Send-Document Send-URI Hold-Job Release-Job Restart-Job Purge-Jobs Set-Job-Attributes Create-Job-Subscription Renew-Subscription Cancel-Subscription Get-Notifications Reprocess-Job Cancel-Current-Job Suspend-Current-Job Resume-Job Cancel-My-Jobs Close-Job CUPS-Move-Job>
|
||||
+ Require user @OWNER @SYSTEM
|
||||
+ Order deny,allow
|
||||
+ </Limit>
|
||||
+
|
||||
+ <Limit CUPS-Get-Document>
|
||||
+ AuthType Default
|
||||
Require user @OWNER @SYSTEM
|
||||
Order deny,allow
|
||||
</Limit>
|
||||
40
meta/recipes-extended/cups/cups/CVE-2023-4504.patch
Normal file
40
meta/recipes-extended/cups/cups/CVE-2023-4504.patch
Normal file
@@ -0,0 +1,40 @@
|
||||
From a9a7daa77699bd58001c25df8a61a8029a217ddf Mon Sep 17 00:00:00 2001
|
||||
From: Zdenek Dohnal <zdohnal@redhat.com>
|
||||
Date: Fri, 1 Sep 2023 16:47:29 +0200
|
||||
Subject: [PATCH] raster-interpret.c: Fix CVE-2023-4504
|
||||
|
||||
We didn't check for end of buffer if it looks there is an escaped
|
||||
character - check for NULL terminator there and if found, return NULL
|
||||
as return value and in `ptr`, because a lone backslash is not
|
||||
a valid PostScript character.
|
||||
|
||||
Upstream-Status: Backport [https://github.com/OpenPrinting/cups/commit/2431caddb7e6a87f04ac90b5c6366ad268b6ff31]
|
||||
CVE: CVE-2023-4504
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
cups/raster-interpret.c | 14 +++++++++++++-
|
||||
1 file changed, 13 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/cups/raster-interpret.c
|
||||
+++ b/cups/raster-interpret.c
|
||||
@@ -1113,7 +1113,19 @@ scan_ps(_cups_ps_stack_t *st, /* I - S
|
||||
|
||||
cur ++;
|
||||
|
||||
- if (*cur == 'b')
|
||||
+ /*
|
||||
+ * Return NULL if we reached NULL terminator, a lone backslash
|
||||
+ * is not a valid character in PostScript.
|
||||
+ */
|
||||
+
|
||||
+ if (!*cur)
|
||||
+ {
|
||||
+ *ptr = NULL;
|
||||
+
|
||||
+ return (NULL);
|
||||
+ }
|
||||
+
|
||||
+ if (*cur == 'b')
|
||||
*valptr++ = '\b';
|
||||
else if (*cur == 'f')
|
||||
*valptr++ = '\f';
|
||||
28
meta/recipes-extended/gawk/gawk/CVE-2023-4156.patch
Normal file
28
meta/recipes-extended/gawk/gawk/CVE-2023-4156.patch
Normal file
@@ -0,0 +1,28 @@
|
||||
From e709eb829448ce040087a3fc5481db6bfcaae212 Mon Sep 17 00:00:00 2001
|
||||
From: "Arnold D. Robbins" <arnold@skeeve.com>
|
||||
Date: Wed, 3 Aug 2022 13:00:54 +0300
|
||||
Subject: [PATCH] Smal bug fix in builtin.c.
|
||||
|
||||
Upstream-Status: Backport [import from ubuntu https://git.launchpad.net/ubuntu/+source/gawk/tree/debian/patches/CVE-2023-4156.patch?h=ubuntu/focal-security
|
||||
Upstream commit https://git.savannah.gnu.org/gitweb/?p=gawk.git;a=commitdiff;h=e709eb829448ce040087a3fc5481db6bfcaae212]
|
||||
CVE: CVE-2023-4156
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
ChangeLog | 6 ++++++
|
||||
builtin.c | 5 ++++-
|
||||
2 files changed, 10 insertions(+), 1 deletion(-)
|
||||
|
||||
--- gawk-5.1.0.orig/builtin.c
|
||||
+++ gawk-5.1.0/builtin.c
|
||||
@@ -957,7 +957,10 @@ check_pos:
|
||||
s1++;
|
||||
n0--;
|
||||
}
|
||||
- if (val >= num_args) {
|
||||
+ // val could be less than zero if someone provides a field width
|
||||
+ // so large that it causes integer overflow. Mainly fuzzers do this,
|
||||
+ // but let's try to be good anyway.
|
||||
+ if (val < 0 || val >= num_args) {
|
||||
toofew = true;
|
||||
break;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user