mirror of
https://git.yoctoproject.org/poky
synced 2026-02-21 00:49:41 +01:00
Compare commits
103 Commits
yocto-5.0.
...
yocto-5.0.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e8674996b | ||
|
|
dd2d3cfc4e | ||
|
|
c1bb6b3d12 | ||
|
|
60d407517c | ||
|
|
d7d531e269 | ||
|
|
6328f52695 | ||
|
|
5b74a8f1a5 | ||
|
|
71aca87ca7 | ||
|
|
1919d76f68 | ||
|
|
568c1afab4 | ||
|
|
f5a9af087b | ||
|
|
bf3b6c9965 | ||
|
|
75922d42b0 | ||
|
|
35b2b34407 | ||
|
|
a905366ee1 | ||
|
|
b5184d1487 | ||
|
|
8f0eab43ed | ||
|
|
2cab0b9833 | ||
|
|
ff75417547 | ||
|
|
b16bf27386 | ||
|
|
11cd8498da | ||
|
|
d101e1410d | ||
|
|
7ff4ef9855 | ||
|
|
7810db935d | ||
|
|
d9c3943da3 | ||
|
|
8951fdb63a | ||
|
|
3ad2146a1b | ||
|
|
4abd3e001c | ||
|
|
dab711ba8b | ||
|
|
302184ed4c | ||
|
|
bc0e06b3b1 | ||
|
|
9c92d62dac | ||
|
|
c973f0e006 | ||
|
|
79aeef5d35 | ||
|
|
18bfeb632b | ||
|
|
b3b8ae2317 | ||
|
|
8c87818a10 | ||
|
|
a6d452646e | ||
|
|
0c4e028627 | ||
|
|
604b1627ed | ||
|
|
255271dfff | ||
|
|
324f29337b | ||
|
|
b33a8abe77 | ||
|
|
4f2287dcc4 | ||
|
|
a2b737539e | ||
|
|
9e31b2eb18 | ||
|
|
ca2c8b88be | ||
|
|
ef4b31498d | ||
|
|
97f732ce9b | ||
|
|
c4a4df3e72 | ||
|
|
7e77ef9016 | ||
|
|
448d31c10b | ||
|
|
121ce25859 | ||
|
|
2cf19dbd79 | ||
|
|
f1125c2cf7 | ||
|
|
ef8c458f20 | ||
|
|
b0e19cc72f | ||
|
|
ba0fd17fa6 | ||
|
|
88bdd0ed92 | ||
|
|
059f795ebe | ||
|
|
9019056557 | ||
|
|
aabe690b3f | ||
|
|
2f480a8669 | ||
|
|
a769ae7a7d | ||
|
|
f2b841423f | ||
|
|
3574b5a9d1 | ||
|
|
b45fdb365d | ||
|
|
4a784d7f74 | ||
|
|
bd0c87a3ac | ||
|
|
5ea0467919 | ||
|
|
2a7d38f814 | ||
|
|
4c457412c8 | ||
|
|
12f14af0bb | ||
|
|
2421e79018 | ||
|
|
251d8b676e | ||
|
|
ad597f4a54 | ||
|
|
c760866299 | ||
|
|
06cbccf616 | ||
|
|
208a66a96b | ||
|
|
2e5bb26c2c | ||
|
|
dba2d4436d | ||
|
|
787bcb1b6f | ||
|
|
c4fd9fd38f | ||
|
|
623cf58e62 | ||
|
|
0eb11e9267 | ||
|
|
d78bf37c9c | ||
|
|
7b08b57feb | ||
|
|
a9224824c3 | ||
|
|
d0302e3d94 | ||
|
|
6d4a516d71 | ||
|
|
a65c2420fd | ||
|
|
574cc67a0d | ||
|
|
be43d55edd | ||
|
|
4456c586d1 | ||
|
|
6d7cfb5461 | ||
|
|
2142f17368 | ||
|
|
a7fdce2a68 | ||
|
|
bf8139e03b | ||
|
|
f0bbacca46 | ||
|
|
2f86700da6 | ||
|
|
f09c292577 | ||
|
|
2ab1bedda9 | ||
|
|
a04f9ab3a5 |
@@ -460,7 +460,7 @@ def uri_replace(ud, uri_find, uri_replace, replacements, d, mirrortarball=None):
|
||||
for k in replacements:
|
||||
uri_replace_decoded[loc] = uri_replace_decoded[loc].replace(k, replacements[k])
|
||||
#bb.note("%s %s %s" % (regexp, uri_replace_decoded[loc], uri_decoded[loc]))
|
||||
result_decoded[loc] = re.sub(regexp, uri_replace_decoded[loc], uri_decoded[loc], 1)
|
||||
result_decoded[loc] = re.sub(regexp, uri_replace_decoded[loc], uri_decoded[loc], count=1)
|
||||
if loc == 2:
|
||||
# Handle path manipulations
|
||||
basename = None
|
||||
|
||||
@@ -344,8 +344,11 @@ class Wget(FetchMethod):
|
||||
opener = urllib.request.build_opener(*handlers)
|
||||
|
||||
try:
|
||||
uri_base = ud.url.split(";")[0]
|
||||
uri = "{}://{}{}".format(urllib.parse.urlparse(uri_base).scheme, ud.host, ud.path)
|
||||
parts = urllib.parse.urlparse(ud.url.split(";")[0])
|
||||
if parts.query:
|
||||
uri = "{}://{}{}?{}".format(parts.scheme, parts.netloc, parts.path, parts.query)
|
||||
else:
|
||||
uri = "{}://{}{}".format(parts.scheme, parts.netloc, parts.path)
|
||||
r = urllib.request.Request(uri)
|
||||
r.get_method = lambda: "HEAD"
|
||||
# Some servers (FusionForge, as used on Alioth) require that the
|
||||
|
||||
@@ -12,7 +12,7 @@ known security vulnerabilities, as tracked by the public
|
||||
database.
|
||||
|
||||
The Yocto Project maintains a `list of known vulnerabilities
|
||||
<https://autobuilder.yocto.io/pub/non-release/patchmetrics/>`__
|
||||
<https://valkyrie.yocto.io/pub/non-release/patchmetrics/>`__
|
||||
for packages in Poky and OE-Core, tracking the evolution of the number of
|
||||
unpatched CVEs and the status of patches. Such information is available for
|
||||
the current development version and for each supported release.
|
||||
@@ -314,7 +314,7 @@ products defined in :term:`CVE_PRODUCT`. Then, for each found CVE:
|
||||
The CVE database is stored in :term:`DL_DIR` and can be inspected using
|
||||
``sqlite3`` command as follows::
|
||||
|
||||
sqlite3 downloads/CVE_CHECK/nvdcve_1.1.db .dump | grep CVE-2021-37462
|
||||
sqlite3 downloads/CVE_CHECK/nvd*.db .dump | grep CVE-2021-37462
|
||||
|
||||
When analyzing CVEs, it is recommended to:
|
||||
|
||||
|
||||
@@ -36,3 +36,4 @@ Release 4.0 (kirkstone)
|
||||
release-notes-4.0.27
|
||||
release-notes-4.0.28
|
||||
release-notes-4.0.29
|
||||
release-notes-4.0.30
|
||||
|
||||
170
documentation/migration-guides/release-notes-4.0.30.rst
Normal file
170
documentation/migration-guides/release-notes-4.0.30.rst
Normal file
@@ -0,0 +1,170 @@
|
||||
Release notes for Yocto-4.0.30 (Kirkstone)
|
||||
------------------------------------------
|
||||
|
||||
Security Fixes in Yocto-4.0.30
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- cups: Fix :cve_nist:`2025-58060` and :cve_nist:`2025-58364`
|
||||
- dpkg: Fix :cve_nist:`2025-6297`
|
||||
- ffmpeg: Fix :cve_nist:`2023-6602`, :cve_nist:`2023-6604`, :cve_nist:`2023-6605`,
|
||||
:cve_nist:`2025-1594` and CVE-2025-7700
|
||||
- git: Fix :cve_nist:`2025-27613`, :cve_nist:`2025-27614`, :cve_nist:`2025-46334`,
|
||||
:cve_nist:`2025-46835` and :cve_nist:`2025-48384`
|
||||
- glib-2.0: Fix :cve_nist:`2025-7039`
|
||||
- glib-2.0: Ignore :cve_nist:`2025-4056`
|
||||
- go: Ignore :cve_nist:`2024-24790` and :cve_nist:`2025-0913`
|
||||
- gstreamer1.0-plugins-base: Fix :cve_nist:`2025-47806`, :cve_nist:`2025-47807` and
|
||||
:cve_nist:`2025-47808`
|
||||
- gstreamer1.0-plugins-good: Fix :cve_nist:`2025-47183` and :cve_nist:`2025-47219`
|
||||
- libarchive: Fix :cve_nist:`2025-5918`
|
||||
- libxslt: Fix :cve_nist:`2023-40403`
|
||||
- openssl: Fix :cve_nist:`2023-50781`
|
||||
- python3: Fix :cve_nist:`2025-8194`
|
||||
- qemu: Ignore :cve_nist:`2024-7730`
|
||||
- sqlite3: Revert "sqlite3: patch CVE-2025-7458"
|
||||
- tiff: Fix :cve_nist:`2024-13978`, :cve_nist:`2025-8176`, :cve_nist:`2025-8177`,
|
||||
:cve_nist:`2025-8534` and :cve_nist:`2025-8851`
|
||||
- vim: Fix :cve_nist:`2025-53905` and :cve_nist:`2025-53906`
|
||||
- wpa-supplicant: Fix :cve_nist:`2022-37660`
|
||||
- xserver-xorg: Fix :cve_nist:`2025-49175`, :cve_nist:`2025-49176`, :cve_nist:`2025-49177`,
|
||||
:cve_nist:`2025-49178`, :cve_nist:`2025-49179` and :cve_nist:`2025-49180`
|
||||
|
||||
|
||||
Fixes in Yocto-4.0.30
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- build-appliance-image: Update to kirkstone head revision
|
||||
- default-distrovars.inc: Fix CONNECTIVITY_CHECK_URIS redirect issue
|
||||
- dev-manual/security-subjects.rst: update mailing lists
|
||||
- gnupg: disable tests to avoid running target binaries at build time
|
||||
- go-helloworld: fix license
|
||||
- insane: Ensure that `src-uri-bad` fails correctly
|
||||
- insane: Improve patch warning/error handling
|
||||
- libubootenv: backport patch to fix unknown type name 'size_t'
|
||||
- llvm: fix typo in CVE-2024-0151.patch
|
||||
- migration-guides: add release notes for 4.0.29
|
||||
- overview-manual/yp-intro.rst: fix broken link to article
|
||||
- poky.conf: bump version for 4.0.30
|
||||
- pulseaudio: Add audio group explicitly
|
||||
- ref-manual/classes.rst: document the testexport class
|
||||
- ref-manual/system-requirements.rst: update supported distributions
|
||||
- ref-manual/variables.rst: document :term:`FIT_CONF_PREFIX` :term:`SPL_DTB_BINARY` variable
|
||||
- ref-manual/variables.rst: expand :term:`IMAGE_OVERHEAD_FACTOR` glossary entry
|
||||
- sdk: The main in the C example should return an int
|
||||
- sudo: remove devtool FIXME comment
|
||||
- systemd: Fix manpage build after :cve_nist:`2025-4598`
|
||||
- vim: not adjust script pathnames for native scripts either
|
||||
- vim: upgrade to 9.1.1652
|
||||
|
||||
|
||||
Known Issues in Yocto-4.0.30
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- N/A
|
||||
|
||||
Contributors to Yocto-4.0.30
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- Antonin Godard
|
||||
- Archana Polampalli
|
||||
- Dan McGregor
|
||||
- Deepak Rathore
|
||||
- Divya Chellam
|
||||
- Erik Lindsten
|
||||
- Guocai He
|
||||
- Gyorgy Sarvari
|
||||
- Hitendra Prajapati
|
||||
- Jan Vermaete
|
||||
- Jiaying Song
|
||||
- Joao Marcos Costa
|
||||
- Kyungjik Min
|
||||
- Lee Chee Yang
|
||||
- Mingli Yu
|
||||
- Peter Marko
|
||||
- Philip Lorenz
|
||||
- Praveen Kumar
|
||||
- Quentin Schulz
|
||||
- Richard Purdie
|
||||
- Steve Sakoman
|
||||
- Vijay Anusuri
|
||||
- Yogita Urade
|
||||
- Youngseok Jeong
|
||||
|
||||
|
||||
Repositories / Downloads for Yocto-4.0.30
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
poky
|
||||
|
||||
- Repository Location: :yocto_git:`/poky`
|
||||
- Branch: :yocto_git:`kirkstone </poky/log/?h=kirkstone>`
|
||||
- Tag: :yocto_git:`yocto-4.0.30 </poky/log/?h=yocto-4.0.30>`
|
||||
- Git Revision: :yocto_git:`51dc9c464de0703bfbc6f1ee71ac9bea20933a45 </poky/commit/?id=51dc9c464de0703bfbc6f1ee71ac9bea20933a45>`
|
||||
- Release Artefact: poky-51dc9c464de0703bfbc6f1ee71ac9bea20933a45
|
||||
- sha: 2b5db0a07598df7684975c0839e6f31515a8e78d366503feb9917ef1ca56c0b2
|
||||
- Download Locations:
|
||||
https://downloads.yoctoproject.org/releases/yocto/yocto-4.0.30/poky-51dc9c464de0703bfbc6f1ee71ac9bea20933a45.tar.bz2
|
||||
https://mirrors.kernel.org/yocto/yocto/yocto-4.0.30/poky-51dc9c464de0703bfbc6f1ee71ac9bea20933a45.tar.bz2
|
||||
|
||||
openembedded-core
|
||||
|
||||
- Repository Location: :oe_git:`/openembedded-core`
|
||||
- Branch: :oe_git:`kirkstone </openembedded-core/log/?h=kirkstone>`
|
||||
- Tag: :oe_git:`yocto-4.0.30 </openembedded-core/log/?h=yocto-4.0.30>`
|
||||
- Git Revision: :oe_git:`d381eeb5e70bd0ce9e78032c909e4a23564f4dd7 </openembedded-core/commit/?id=d381eeb5e70bd0ce9e78032c909e4a23564f4dd7>`
|
||||
- Release Artefact: oecore-d381eeb5e70bd0ce9e78032c909e4a23564f4dd7
|
||||
- sha: 022ab4ef5ac59ac3f01a9dacd8b1d6310cc117c6bed2e86e195ced88e0689c85
|
||||
- Download Locations:
|
||||
https://downloads.yoctoproject.org/releases/yocto/yocto-4.0.30/oecore-d381eeb5e70bd0ce9e78032c909e4a23564f4dd7.tar.bz2
|
||||
https://mirrors.kernel.org/yocto/yocto/yocto-4.0.30/oecore-d381eeb5e70bd0ce9e78032c909e4a23564f4dd7.tar.bz2
|
||||
|
||||
meta-mingw
|
||||
|
||||
- Repository Location: :yocto_git:`/meta-mingw`
|
||||
- Branch: :yocto_git:`kirkstone </meta-mingw/log/?h=kirkstone>`
|
||||
- Tag: :yocto_git:`yocto-4.0.30 </meta-mingw/log/?h=yocto-4.0.30>`
|
||||
- Git Revision: :yocto_git:`87c22abb1f11be430caf4372e6b833dc7d77564e </meta-mingw/commit/?id=87c22abb1f11be430caf4372e6b833dc7d77564e>`
|
||||
- Release Artefact: meta-mingw-87c22abb1f11be430caf4372e6b833dc7d77564e
|
||||
- sha: f0bc4873e2e0319fb9d6d6ab9b98eb3f89664d4339a167d2db6a787dd12bc1a8
|
||||
- Download Locations:
|
||||
https://downloads.yoctoproject.org/releases/yocto/yocto-4.0.30/meta-mingw-87c22abb1f11be430caf4372e6b833dc7d77564e.tar.bz2
|
||||
https://mirrors.kernel.org/yocto/yocto/yocto-4.0.30/meta-mingw-87c22abb1f11be430caf4372e6b833dc7d77564e.tar.bz2
|
||||
|
||||
meta-gplv2
|
||||
|
||||
- Repository Location: :yocto_git:`/meta-gplv2`
|
||||
- Branch: :yocto_git:`kirkstone </meta-gplv2/log/?h=kirkstone>`
|
||||
- Tag: :yocto_git:`yocto-4.0.30 </meta-gplv2/log/?h=yocto-4.0.30>`
|
||||
- Git Revision: :yocto_git:`d2f8b5cdb285b72a4ed93450f6703ca27aa42e8a </meta-gplv2/commit/?id=d2f8b5cdb285b72a4ed93450f6703ca27aa42e8a>`
|
||||
- Release Artefact: meta-gplv2-d2f8b5cdb285b72a4ed93450f6703ca27aa42e8a
|
||||
- sha: c386f59f8a672747dc3d0be1d4234b6039273d0e57933eb87caa20f56b9cca6d
|
||||
- Download Locations:
|
||||
https://downloads.yoctoproject.org/releases/yocto/yocto-4.0.30/meta-gplv2-d2f8b5cdb285b72a4ed93450f6703ca27aa42e8a.tar.bz2
|
||||
https://mirrors.kernel.org/yocto/yocto/yocto-4.0.30/meta-gplv2-d2f8b5cdb285b72a4ed93450f6703ca27aa42e8a.tar.bz2
|
||||
|
||||
bitbake
|
||||
|
||||
- Repository Location: :oe_git:`/bitbake`
|
||||
- Branch: :oe_git:`2.0 </bitbake/log/?h=2.0>`
|
||||
- Tag: :oe_git:`yocto-4.0.30 </bitbake/log/?h=yocto-4.0.30>`
|
||||
- Git Revision: :oe_git:`8e2d1f8de055549b2101614d85454fcd1d0f94b2 </bitbake/commit/?id=8e2d1f8de055549b2101614d85454fcd1d0f94b2>`
|
||||
- Release Artefact: bitbake-8e2d1f8de055549b2101614d85454fcd1d0f94b2
|
||||
- sha: fad4e7699bae62082118e89785324b031b0af0743064caee87c91ba28549afb0
|
||||
- Download Locations:
|
||||
https://downloads.yoctoproject.org/releases/yocto/yocto-4.0.30/bitbake-8e2d1f8de055549b2101614d85454fcd1d0f94b2.tar.bz2
|
||||
https://mirrors.kernel.org/yocto/yocto/yocto-4.0.30/bitbake-8e2d1f8de055549b2101614d85454fcd1d0f94b2.tar.bz2
|
||||
|
||||
meta-yocto
|
||||
|
||||
- Repository Location: :yocto_git:`/meta-yocto`
|
||||
- Branch: :yocto_git:`kirkstone </meta-yocto/log/?h=kirkstone>`
|
||||
- Tag: :yocto_git:`yocto-4.0.30 </meta-yocto/log/?h=yocto-4.0.30>`
|
||||
- Git Revision: :yocto_git:`edf7950e4d81dd31f29a58acdd8022dabd2be494 </meta-yocto/commit/?id=edf7950e4d81dd31f29a58acdd8022dabd2be494>`
|
||||
|
||||
yocto-docs
|
||||
|
||||
- Repository Location: :yocto_git:`/yocto-docs`
|
||||
- Branch: :yocto_git:`kirkstone </yocto-docs/log/?h=kirkstone>`
|
||||
- Tag: :yocto_git:`yocto-4.0.30 </yocto-docs/log/?h=yocto-4.0.30>`
|
||||
- Git Revision: :yocto_git:`71a3933c609ce73ff07e5be48d9e7b03f22ef8d7 </yocto-docs/commit/?id=71a3933c609ce73ff07e5be48d9e7b03f22ef8d7>`
|
||||
|
||||
@@ -172,11 +172,12 @@ Here are challenges you might encounter when developing using the Yocto Project:
|
||||
changes on the development system within the BitBake environment and
|
||||
then deploying only the updated packages to the target.
|
||||
|
||||
The Yocto Project :term:`OpenEmbedded Build System`
|
||||
produces packages
|
||||
in standard formats (i.e. RPM, DEB, IPK, and TAR). You can deploy
|
||||
these packages into the running system on the target by using
|
||||
utilities on the target such as ``rpm`` or ``ipk``.
|
||||
The Yocto Project :term:`OpenEmbedded Build System` produces packages
|
||||
in standard formats (i.e. RPM, DEB and/or IPK). If you included the
|
||||
runtime package management feature in your image, you can deploy
|
||||
these packages into the running system on the target by using the
|
||||
corresponding utilities on the target such as
|
||||
``rpm``/``dnf``, ``dpkg``/``apt`` or ``opkg``.
|
||||
|
||||
- *Initial Build Times Can be Significant:* Long initial build times
|
||||
are unfortunately unavoidable due to the large number of packages
|
||||
|
||||
@@ -355,8 +355,12 @@ file for details about how to enable this mechanism in your configuration
|
||||
file, how to disable it for specific recipes, and how to share ``ccache``
|
||||
files between builds.
|
||||
|
||||
However, using the class can lead to unexpected side-effects. Thus, using
|
||||
this class is not recommended.
|
||||
Recipes can also explicitly disable `Ccache` support even when the
|
||||
:ref:`ref-classes-ccache` class is enabled, by setting the
|
||||
:term:`CCACHE_DISABLE` variable to "1".
|
||||
|
||||
Using the :ref:`ref-classes-ccache` class can lead to unexpected side-effects.
|
||||
Using this class is not recommended.
|
||||
|
||||
.. _ref-classes-chrpath:
|
||||
|
||||
@@ -909,6 +913,14 @@ software that uses the GNU ``gettext`` internationalization and localization
|
||||
system. All recipes building software that use ``gettext`` should inherit this
|
||||
class.
|
||||
|
||||
This class will configure recipes to build translations *unless*:
|
||||
|
||||
- the :term:`USE_NLS` variable is set to ``no``, or
|
||||
|
||||
- the :term:`INHIBIT_DEFAULT_DEPS` variable is set and the recipe inheriting
|
||||
the :ref:`ref-classes-gettext` class does not also inherit the
|
||||
:ref:`ref-classes-cross-canadian` class.
|
||||
|
||||
.. _ref-classes-github-releases:
|
||||
|
||||
``github-releases``
|
||||
@@ -2692,6 +2704,25 @@ The :ref:`ref-classes-recipe_sanity` class checks for the presence of any host s
|
||||
recipe prerequisites that might affect the build (e.g. variables that
|
||||
are set or software that is present).
|
||||
|
||||
.. _ref-classes-relative_symlinks:
|
||||
|
||||
``relative_symlinks``
|
||||
=====================
|
||||
|
||||
The :ref:`ref-classes-relative_symlinks` class walks the symbolic links in the
|
||||
:term:`D` directory and replaces links pointing to absolute paths to relative
|
||||
paths. This is occasionally used in some recipes that create wrong symbolic
|
||||
links when their :ref:`ref-classes-native` version is built, and/or would cause
|
||||
breakage in the :ref:`overview-manual/concepts:shared state cache`.
|
||||
|
||||
For example, if the following symbolic link is found in :term:`D`::
|
||||
|
||||
/usr/bin/foo -> /sbin/bar
|
||||
|
||||
It is replaced by::
|
||||
|
||||
/usr/bin/foo -> ../../sbin/bar
|
||||
|
||||
.. _ref-classes-relocatable:
|
||||
|
||||
``relocatable``
|
||||
@@ -3363,22 +3394,51 @@ imitates.
|
||||
``uninative``
|
||||
=============
|
||||
|
||||
Attempts to isolate the build system from the host distribution's C
|
||||
library in order to make re-use of native shared state artifacts across
|
||||
different host distributions practical. With this class enabled, a
|
||||
tarball containing a pre-built C library is downloaded at the start of
|
||||
the build. In the Poky reference distribution this is enabled by default
|
||||
through ``meta/conf/distro/include/yocto-uninative.inc``. Other
|
||||
distributions that do not derive from poky can also
|
||||
"``require conf/distro/include/yocto-uninative.inc``" to use this.
|
||||
Alternatively if you prefer, you can build the uninative-tarball recipe
|
||||
yourself, publish the resulting tarball (e.g. via HTTP) and set
|
||||
``UNINATIVE_URL`` and ``UNINATIVE_CHECKSUM`` appropriately. For an
|
||||
example, see the ``meta/conf/distro/include/yocto-uninative.inc``.
|
||||
The :ref:`ref-classes-uninative` class allows binaries to run on systems with
|
||||
older or newer :wikipedia:`Glibc <Glibc>` versions. This means
|
||||
:ref:`ref-classes-native` recipe :ref:`overview-manual/concepts:shared state
|
||||
cache` can be shared among different host distributions of different versions,
|
||||
i.e. the :ref:`overview-manual/concepts:shared state cache` is "universal".
|
||||
|
||||
The :ref:`ref-classes-uninative` class is also used unconditionally by the extensible
|
||||
SDK. When building the extensible SDK, ``uninative-tarball`` is built
|
||||
and the resulting tarball is included within the SDK.
|
||||
To allow this to work, the dynamic loader is changed to our own :manpage:`ld.so
|
||||
<ld.so.8>` when binaries are compiled using the
|
||||
``--dynamic-linker`` option. This means when the binary is executed, it finds
|
||||
our own :manpage:`ld.so <ld.so.8>` and that loader has a modified search path
|
||||
which finds a newer Glibc version.
|
||||
|
||||
The linking of the binaries is not changed at link time since the
|
||||
headers on the system wouldn't match the newer Glibc and this causes
|
||||
obtuse failures. Changing the loader is effectively the same as if the
|
||||
system had a Glibc upgrade after the binary was compiled, so it is a
|
||||
mechanism supported by upstream.
|
||||
|
||||
One caveat to this approach is that the uninative Glibc binary must be
|
||||
equal to or newer in version to the versions on all the systems using
|
||||
the common :ref:`overview-manual/concepts:shared state cache`. This is why
|
||||
:ref:`ref-classes-uninative` is regularly changed on the development and stable
|
||||
branches.
|
||||
|
||||
Another potential issue is static linking: static libraries created on
|
||||
a system with a new Glibc version may have symbols not present in older
|
||||
versions, which would then fail during linking on older systems. This
|
||||
is one reason we don't use static linking for our :ref:`ref-classes-native`
|
||||
binaries.
|
||||
|
||||
With this class enabled, a tarball containing a pre-built C library is
|
||||
downloaded at the start of the build. In the Poky reference distribution this is
|
||||
enabled by default through :oe_git:`meta/conf/distro/include/yocto-uninative.inc
|
||||
</openembedded-core/tree/meta/conf/distro/include/yocto-uninative.inc>`. Other distributions that do
|
||||
not derive from Poky can also "``require conf/distro/include/yocto-uninative.inc``"
|
||||
to use this. Alternatively if you prefer, you can build the uninative-tarball
|
||||
recipe yourself, publish the resulting tarball (e.g. via HTTP) and set
|
||||
:term:`UNINATIVE_URL` and :term:`UNINATIVE_CHECKSUM` appropriately. For an
|
||||
example, see :oe_git:`meta/conf/distro/include/yocto-uninative.inc
|
||||
</openembedded-core/tree/meta/conf/distro/include/yocto-uninative.inc>`.
|
||||
|
||||
The :ref:`ref-classes-uninative` class is also used unconditionally by the
|
||||
:doc:`extensible SDK </sdk-manual/extensible>`. When building the extensible
|
||||
SDK, ``uninative-tarball`` is built and the resulting tarball is included within
|
||||
the SDK.
|
||||
|
||||
.. _ref-classes-update-alternatives:
|
||||
|
||||
|
||||
@@ -1458,6 +1458,11 @@ system and gives an overview of their function and contents.
|
||||
:term:`CC`
|
||||
The minimal command and arguments used to run the C compiler.
|
||||
|
||||
:term:`CCACHE_DISABLE`
|
||||
When inheriting the :ref:`ref-classes-ccache` class, the
|
||||
:term:`CCACHE_DISABLE` variable can be set to "1" in a recipe to disable
|
||||
`Ccache` support. This is useful when the recipe is known to not support it.
|
||||
|
||||
:term:`CCLD`
|
||||
The minimal command and arguments used to run the linker when the C
|
||||
compiler is being used as the linker.
|
||||
@@ -5224,7 +5229,7 @@ system and gives an overview of their function and contents.
|
||||
information on how this variable is used.
|
||||
|
||||
:term:`LAYERDEPENDS`
|
||||
Lists the layers, separated by spaces, on which this recipe depends.
|
||||
Lists the layers, separated by spaces, on which this layer depends.
|
||||
Optionally, you can specify a specific layer version for a dependency
|
||||
by adding it to the end of the layer name. Here is an example::
|
||||
|
||||
@@ -7438,6 +7443,16 @@ system and gives an overview of their function and contents.
|
||||
:term:`REPODIR`
|
||||
See :term:`bitbake:REPODIR` in the BitBake manual.
|
||||
|
||||
:term:`REQUIRED_COMBINED_FEATURES`
|
||||
When inheriting the :ref:`ref-classes-features_check` class, this variable
|
||||
identifies combined features (the intersection of :term:`MACHINE_FEATURES`
|
||||
and :term:`DISTRO_FEATURES`) that must exist in the current configuration
|
||||
in order for the :term:`OpenEmbedded Build System` to build the recipe. In
|
||||
other words, if the :term:`REQUIRED_COMBINED_FEATURES` variable lists a
|
||||
feature that does not appear in :term:`COMBINED_FEATURES` within the
|
||||
current configuration, then the recipe will be skipped, and if the build
|
||||
system attempts to build the recipe then an error will be triggered.
|
||||
|
||||
:term:`REQUIRED_DISTRO_FEATURES`
|
||||
When inheriting the :ref:`ref-classes-features_check`
|
||||
class, this variable identifies distribution features that must exist
|
||||
@@ -7448,6 +7463,32 @@ system and gives an overview of their function and contents.
|
||||
the recipe will be skipped, and if the build system attempts to build
|
||||
the recipe then an error will be triggered.
|
||||
|
||||
:term:`REQUIRED_IMAGE_FEATURES`
|
||||
When inheriting the :ref:`ref-classes-features_check` class, this variable
|
||||
identifies image features that must exist in the current
|
||||
configuration in order for the :term:`OpenEmbedded Build System` to build
|
||||
the recipe. In other words, if the :term:`REQUIRED_IMAGE_FEATURES` variable
|
||||
lists a feature that does not appear in :term:`IMAGE_FEATURES` within the
|
||||
current configuration, then the recipe will be skipped, and if the build
|
||||
system attempts to build the recipe then an error will be triggered.
|
||||
|
||||
Compared to other ``REQUIRED_*_FEATURES`` variables, the
|
||||
:term:`REQUIRED_IMAGE_FEATURES` varible only targets image recipes, as the
|
||||
:term:`IMAGE_FEATURES` variable is handled by the :ref:`ref-classes-core-image`
|
||||
class). However, the :term:`REQUIRED_IMAGE_FEATURES` varible can also be
|
||||
set from a :term:`Configuration File`, such as a distro
|
||||
configuration file, if the list of required image features should apply to
|
||||
all images using this :term:`DISTRO`.
|
||||
|
||||
:term:`REQUIRED_MACHINE_FEATURES`
|
||||
When inheriting the :ref:`ref-classes-features_check` class, this variable
|
||||
identifies :term:`MACHINE_FEATURES` that must exist in the current
|
||||
configuration in order for the :term:`OpenEmbedded Build System` to build
|
||||
the recipe. In other words, if the :term:`REQUIRED_MACHINE_FEATURES` variable
|
||||
lists a feature that does not appear in :term:`MACHINE_FEATURES` within the
|
||||
current configuration, then the recipe will be skipped, and if the build
|
||||
system attempts to build the recipe then an error will be triggered.
|
||||
|
||||
:term:`REQUIRED_VERSION`
|
||||
If there are multiple versions of a recipe available, this variable
|
||||
determines which version should be given preference.
|
||||
@@ -10178,6 +10219,22 @@ system and gives an overview of their function and contents.
|
||||
passes and uses "all" for the target during the U-Boot building
|
||||
process.
|
||||
|
||||
:term:`UNINATIVE_CHECKSUM`
|
||||
When inheriting the :ref:`ref-classes-uninative` class, the
|
||||
:term:`UNINATIVE_CHECKSUM` variable flags contain the checksums of the
|
||||
uninative tarball as specified by the :term:`UNINATIVE_URL` variable.
|
||||
There should be one checksum per tarballs published at
|
||||
:term:`UNINATIVE_URL`, which match architectures. For example::
|
||||
|
||||
UNINATIVE_CHECKSUM[aarch64] ?= "812045d826b7fda88944055e8526b95a5a9440bfef608d5b53fd52faab49bf85"
|
||||
UNINATIVE_CHECKSUM[i686] ?= "5cc28efd0c15a75de4bcb147c6cce65f1c1c9d442173a220f08427f40a3ffa09"
|
||||
UNINATIVE_CHECKSUM[x86_64] ?= "4c03d1ed2b7b4e823aca4a1a23d8f2e322f1770fc10e859adcede5777aff4f3a"
|
||||
|
||||
:term:`UNINATIVE_URL`
|
||||
When inheriting the :ref:`ref-classes-uninative` class, the
|
||||
:term:`UNINATIVE_URL` variable contains the URL where the uninative
|
||||
tarballs are published.
|
||||
|
||||
:term:`UNKNOWN_CONFIGURE_OPT_IGNORE`
|
||||
Specifies a list of options that, if reported by the configure script
|
||||
as being invalid, should not generate a warning during the
|
||||
@@ -10268,6 +10325,18 @@ system and gives an overview of their function and contents.
|
||||
the Yocto Project Development Tasks Manual for information on how to
|
||||
use this variable.
|
||||
|
||||
:term:`USE_NLS`
|
||||
Determine if language translations should be built for recipes that can
|
||||
build them. This variable can be equal to:
|
||||
|
||||
- ``yes``: translations are enabled.
|
||||
- ``no``: translation are disabled.
|
||||
|
||||
Recipes can use the value of this variable to enable language
|
||||
translations in their build. Classes such as :ref:`ref-classes-gettext`
|
||||
use the value of this variable to enable :wikipedia:`Gettext <Gettext>`
|
||||
support.
|
||||
|
||||
:term:`USE_VT`
|
||||
When using
|
||||
:ref:`SysVinit <dev-manual/new-recipe:enabling system services>`,
|
||||
|
||||
@@ -113,7 +113,7 @@ If ``OEQA_DEBUGGING_SAVED_OUTPUT`` is set, any differing packages will be saved
|
||||
here. The test is also able to run the ``diffoscope`` command on the output to
|
||||
generate HTML files showing the differences between the packages, to aid
|
||||
debugging. On the Autobuilder, these appear under
|
||||
https://autobuilder.yocto.io/pub/repro-fail/ in the form ``oe-reproducible +
|
||||
https://valkyrie.yocto.io/pub/repro-fail/ in the form ``oe-reproducible +
|
||||
<date> + <random ID>``, e.g. ``oe-reproducible-20200202-1lm8o1th``.
|
||||
|
||||
The project's current reproducibility status can be seen at
|
||||
|
||||
@@ -69,7 +69,7 @@ box to "generate an email to QA" is also checked.
|
||||
When the build completes, an email is sent out using the ``send-qa-email``
|
||||
script in the :yocto_git:`yocto-autobuilder-helper </yocto-autobuilder-helper>`
|
||||
repository to the list of people configured for that release. Release builds
|
||||
are placed into a directory in https://autobuilder.yocto.io/pub/releases on the
|
||||
are placed into a directory in https://valkyrie.yocto.io/pub/releases on the
|
||||
Autobuilder which is included in the email. The process from here is
|
||||
more manual and control is effectively passed to release engineering.
|
||||
The next steps include:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
DISTRO = "poky"
|
||||
DISTRO_NAME = "Poky (Yocto Project Reference Distro)"
|
||||
DISTRO_VERSION = "5.0.13"
|
||||
DISTRO_VERSION = "5.0.14"
|
||||
DISTRO_CODENAME = "scarthgap"
|
||||
SDK_VENDOR = "-pokysdk"
|
||||
SDK_VERSION = "${@d.getVar('DISTRO_VERSION').replace('snapshot-${METADATA_REVISION}', 'snapshot')}"
|
||||
|
||||
@@ -520,8 +520,8 @@ python () {
|
||||
bb.fatal('This recipe does not have the LICENSE field set (%s)' % pn)
|
||||
|
||||
if bb.data.inherits_class('license', d):
|
||||
check_license_format(d)
|
||||
unmatched_license_flags = check_license_flags(d)
|
||||
oe.license.check_license_format(d)
|
||||
unmatched_license_flags = oe.license.check_license_flags(d)
|
||||
if unmatched_license_flags:
|
||||
for unmatched in unmatched_license_flags:
|
||||
message = "Has a restricted license '%s' which is not listed in your LICENSE_FLAGS_ACCEPTED." % unmatched
|
||||
@@ -565,37 +565,10 @@ python () {
|
||||
|
||||
bad_licenses = (d.getVar('INCOMPATIBLE_LICENSE') or "").split()
|
||||
|
||||
check_license = False if pn.startswith("nativesdk-") else True
|
||||
for t in ["-native", "-cross-${TARGET_ARCH}", "-cross-initial-${TARGET_ARCH}",
|
||||
"-crosssdk-${SDK_SYS}", "-crosssdk-initial-${SDK_SYS}",
|
||||
"-cross-canadian-${TRANSLATED_TARGET_ARCH}"]:
|
||||
if pn.endswith(d.expand(t)):
|
||||
check_license = False
|
||||
if pn.startswith("gcc-source-"):
|
||||
check_license = False
|
||||
|
||||
if check_license and bad_licenses:
|
||||
bad_licenses = expand_wildcard_licenses(d, bad_licenses)
|
||||
|
||||
exceptions = (d.getVar("INCOMPATIBLE_LICENSE_EXCEPTIONS") or "").split()
|
||||
|
||||
for lic_exception in exceptions:
|
||||
if ":" in lic_exception:
|
||||
lic_exception = lic_exception.split(":")[1]
|
||||
if lic_exception in oe.license.obsolete_license_list():
|
||||
bb.fatal("Obsolete license %s used in INCOMPATIBLE_LICENSE_EXCEPTIONS" % lic_exception)
|
||||
|
||||
pkgs = d.getVar('PACKAGES').split()
|
||||
skipped_pkgs = {}
|
||||
unskipped_pkgs = []
|
||||
for pkg in pkgs:
|
||||
remaining_bad_licenses = oe.license.apply_pkg_license_exception(pkg, bad_licenses, exceptions)
|
||||
|
||||
incompatible_lic = incompatible_license(d, remaining_bad_licenses, pkg)
|
||||
if incompatible_lic:
|
||||
skipped_pkgs[pkg] = incompatible_lic
|
||||
else:
|
||||
unskipped_pkgs.append(pkg)
|
||||
pkgs = d.getVar('PACKAGES').split()
|
||||
if pkgs:
|
||||
skipped_pkgs = oe.license.skip_incompatible_package_licenses(d, pkgs)
|
||||
unskipped_pkgs = [p for p in pkgs if p not in skipped_pkgs]
|
||||
|
||||
if unskipped_pkgs:
|
||||
for pkg in skipped_pkgs:
|
||||
@@ -604,7 +577,7 @@ python () {
|
||||
for pkg in unskipped_pkgs:
|
||||
bb.debug(1, "Including the package %s" % pkg)
|
||||
else:
|
||||
incompatible_lic = incompatible_license(d, bad_licenses)
|
||||
incompatible_lic = oe.license.incompatible_license(d, bad_licenses)
|
||||
for pkg in skipped_pkgs:
|
||||
incompatible_lic += skipped_pkgs[pkg]
|
||||
incompatible_lic = sorted(list(set(incompatible_lic)))
|
||||
|
||||
@@ -255,171 +255,6 @@ def find_license_files(d):
|
||||
|
||||
return lic_files_paths
|
||||
|
||||
def return_spdx(d, license):
|
||||
"""
|
||||
This function returns the spdx mapping of a license if it exists.
|
||||
"""
|
||||
return d.getVarFlag('SPDXLICENSEMAP', license)
|
||||
|
||||
def canonical_license(d, license):
|
||||
"""
|
||||
Return the canonical (SPDX) form of the license if available (so GPLv3
|
||||
becomes GPL-3.0-only) or the passed license if there is no canonical form.
|
||||
"""
|
||||
return d.getVarFlag('SPDXLICENSEMAP', license) or license
|
||||
|
||||
def expand_wildcard_licenses(d, wildcard_licenses):
|
||||
"""
|
||||
There are some common wildcard values users may want to use. Support them
|
||||
here.
|
||||
"""
|
||||
licenses = set(wildcard_licenses)
|
||||
mapping = {
|
||||
"AGPL-3.0*" : ["AGPL-3.0-only", "AGPL-3.0-or-later"],
|
||||
"GPL-3.0*" : ["GPL-3.0-only", "GPL-3.0-or-later"],
|
||||
"LGPL-3.0*" : ["LGPL-3.0-only", "LGPL-3.0-or-later"],
|
||||
}
|
||||
for k in mapping:
|
||||
if k in wildcard_licenses:
|
||||
licenses.remove(k)
|
||||
for item in mapping[k]:
|
||||
licenses.add(item)
|
||||
|
||||
for l in licenses:
|
||||
if l in oe.license.obsolete_license_list():
|
||||
bb.fatal("Error, %s is an obsolete license, please use an SPDX reference in INCOMPATIBLE_LICENSE" % l)
|
||||
if "*" in l:
|
||||
bb.fatal("Error, %s is an invalid license wildcard entry" % l)
|
||||
|
||||
return list(licenses)
|
||||
|
||||
def incompatible_license_contains(license, truevalue, falsevalue, d):
|
||||
license = canonical_license(d, license)
|
||||
bad_licenses = (d.getVar('INCOMPATIBLE_LICENSE') or "").split()
|
||||
bad_licenses = expand_wildcard_licenses(d, bad_licenses)
|
||||
return truevalue if license in bad_licenses else falsevalue
|
||||
|
||||
def incompatible_pkg_license(d, dont_want_licenses, license):
|
||||
# Handles an "or" or two license sets provided by
|
||||
# flattened_licenses(), pick one that works if possible.
|
||||
def choose_lic_set(a, b):
|
||||
return a if all(oe.license.license_ok(canonical_license(d, lic),
|
||||
dont_want_licenses) for lic in a) else b
|
||||
|
||||
try:
|
||||
licenses = oe.license.flattened_licenses(license, choose_lic_set)
|
||||
except oe.license.LicenseError as exc:
|
||||
bb.fatal('%s: %s' % (d.getVar('P'), exc))
|
||||
|
||||
incompatible_lic = []
|
||||
for l in licenses:
|
||||
license = canonical_license(d, l)
|
||||
if not oe.license.license_ok(license, dont_want_licenses):
|
||||
incompatible_lic.append(license)
|
||||
|
||||
return sorted(incompatible_lic)
|
||||
|
||||
def incompatible_license(d, dont_want_licenses, package=None):
|
||||
"""
|
||||
This function checks if a recipe has only incompatible licenses. It also
|
||||
take into consideration 'or' operand. dont_want_licenses should be passed
|
||||
as canonical (SPDX) names.
|
||||
"""
|
||||
import oe.license
|
||||
license = d.getVar("LICENSE:%s" % package) if package else None
|
||||
if not license:
|
||||
license = d.getVar('LICENSE')
|
||||
|
||||
return incompatible_pkg_license(d, dont_want_licenses, license)
|
||||
|
||||
def check_license_flags(d):
|
||||
"""
|
||||
This function checks if a recipe has any LICENSE_FLAGS that
|
||||
aren't acceptable.
|
||||
|
||||
If it does, it returns the all LICENSE_FLAGS missing from the list
|
||||
of acceptable license flags, or all of the LICENSE_FLAGS if there
|
||||
is no list of acceptable flags.
|
||||
|
||||
If everything is is acceptable, it returns None.
|
||||
"""
|
||||
|
||||
def license_flag_matches(flag, acceptlist, pn):
|
||||
"""
|
||||
Return True if flag matches something in acceptlist, None if not.
|
||||
|
||||
Before we test a flag against the acceptlist, we append _${PN}
|
||||
to it. We then try to match that string against the
|
||||
acceptlist. This covers the normal case, where we expect
|
||||
LICENSE_FLAGS to be a simple string like 'commercial', which
|
||||
the user typically matches exactly in the acceptlist by
|
||||
explicitly appending the package name e.g 'commercial_foo'.
|
||||
If we fail the match however, we then split the flag across
|
||||
'_' and append each fragment and test until we either match or
|
||||
run out of fragments.
|
||||
"""
|
||||
flag_pn = ("%s_%s" % (flag, pn))
|
||||
for candidate in acceptlist:
|
||||
if flag_pn == candidate:
|
||||
return True
|
||||
|
||||
flag_cur = ""
|
||||
flagments = flag_pn.split("_")
|
||||
flagments.pop() # we've already tested the full string
|
||||
for flagment in flagments:
|
||||
if flag_cur:
|
||||
flag_cur += "_"
|
||||
flag_cur += flagment
|
||||
for candidate in acceptlist:
|
||||
if flag_cur == candidate:
|
||||
return True
|
||||
return False
|
||||
|
||||
def all_license_flags_match(license_flags, acceptlist):
|
||||
""" Return all unmatched flags, None if all flags match """
|
||||
pn = d.getVar('PN')
|
||||
split_acceptlist = acceptlist.split()
|
||||
flags = []
|
||||
for flag in license_flags.split():
|
||||
if not license_flag_matches(flag, split_acceptlist, pn):
|
||||
flags.append(flag)
|
||||
return flags if flags else None
|
||||
|
||||
license_flags = d.getVar('LICENSE_FLAGS')
|
||||
if license_flags:
|
||||
acceptlist = d.getVar('LICENSE_FLAGS_ACCEPTED')
|
||||
if not acceptlist:
|
||||
return license_flags.split()
|
||||
unmatched_flags = all_license_flags_match(license_flags, acceptlist)
|
||||
if unmatched_flags:
|
||||
return unmatched_flags
|
||||
return None
|
||||
|
||||
def check_license_format(d):
|
||||
"""
|
||||
This function checks if LICENSE is well defined,
|
||||
Validate operators in LICENSES.
|
||||
No spaces are allowed between LICENSES.
|
||||
"""
|
||||
pn = d.getVar('PN')
|
||||
licenses = d.getVar('LICENSE')
|
||||
from oe.license import license_operator, license_operator_chars, license_pattern
|
||||
|
||||
elements = list(filter(lambda x: x.strip(), license_operator.split(licenses)))
|
||||
for pos, element in enumerate(elements):
|
||||
if license_pattern.match(element):
|
||||
if pos > 0 and license_pattern.match(elements[pos - 1]):
|
||||
oe.qa.handle_error('license-format',
|
||||
'%s: LICENSE value "%s" has an invalid format - license names ' \
|
||||
'must be separated by the following characters to indicate ' \
|
||||
'the license selection: %s' %
|
||||
(pn, licenses, license_operator_chars), d)
|
||||
elif not license_operator.match(element):
|
||||
oe.qa.handle_error('license-format',
|
||||
'%s: LICENSE value "%s" has an invalid separator "%s" that is not ' \
|
||||
'in the valid list of separators (%s)' %
|
||||
(pn, licenses, element, license_operator_chars), d)
|
||||
|
||||
SSTATETASKS += "do_populate_lic"
|
||||
do_populate_lic[sstate-inputdirs] = "${LICSSTATEDIR}"
|
||||
do_populate_lic[sstate-outputdirs] = "${LICENSE_DIRECTORY}/"
|
||||
|
||||
@@ -652,10 +652,17 @@ python do_prepare_recipe_sysroot () {
|
||||
addtask do_prepare_recipe_sysroot before do_configure after do_fetch
|
||||
|
||||
python staging_taskhandler() {
|
||||
EXCLUDED_TASKS = (
|
||||
"do_prepare_recipe_sysroot",
|
||||
"do_create_spdx",
|
||||
)
|
||||
bbtasks = e.tasklist
|
||||
for task in bbtasks:
|
||||
if task in EXCLUDED_TASKS:
|
||||
continue
|
||||
|
||||
deps = d.getVarFlag(task, "depends")
|
||||
if task != 'do_prepare_recipe_sysroot' and (task == "do_configure" or (deps and "populate_sysroot" in deps)):
|
||||
if task == "do_configure" or (deps and "populate_sysroot" in deps):
|
||||
d.prependVarFlag(task, "prefuncs", "extend_recipe_sysroot ")
|
||||
}
|
||||
staging_taskhandler[eventmask] = "bb.event.RecipeTaskPreProcess"
|
||||
|
||||
@@ -30,6 +30,9 @@ BAREMETAL_BINNAME ?= "hello_baremetal_${MACHINE}"
|
||||
IMAGE_LINK_NAME ?= "baremetal-helloworld-image-${MACHINE}"
|
||||
IMAGE_NAME_SUFFIX ?= ""
|
||||
|
||||
IMAGE_OUTPUT_MANIFEST_DIR = "${WORKDIR}/deploy-image-output-manifest"
|
||||
IMAGE_OUTPUT_MANIFEST = "${IMAGE_OUTPUT_MANIFEST_DIR}/manifest.json"
|
||||
|
||||
do_rootfs[dirs] = "${IMGDEPLOYDIR} ${DEPLOY_DIR_IMAGE}"
|
||||
|
||||
do_image(){
|
||||
@@ -37,8 +40,28 @@ do_image(){
|
||||
install ${D}/${base_libdir}/firmware/${BAREMETAL_BINNAME}.elf ${IMGDEPLOYDIR}/${IMAGE_LINK_NAME}.elf
|
||||
}
|
||||
|
||||
do_image_complete(){
|
||||
:
|
||||
python do_image_complete(){
|
||||
from pathlib import Path
|
||||
import json
|
||||
|
||||
data = {
|
||||
"taskname": "do_image",
|
||||
"imagetype": "baremetal-image",
|
||||
"images": []
|
||||
}
|
||||
|
||||
img_deploy_dir = Path(d.getVar("IMGDEPLOYDIR"))
|
||||
|
||||
for child in img_deploy_dir.iterdir():
|
||||
if not child.is_file() or child.is_symlink():
|
||||
continue
|
||||
|
||||
data["images"].append({
|
||||
"filename": child.name,
|
||||
})
|
||||
|
||||
with open(d.getVar("IMAGE_OUTPUT_MANIFEST"), "w") as f:
|
||||
json.dump([data], f)
|
||||
}
|
||||
|
||||
python do_rootfs(){
|
||||
@@ -62,6 +85,7 @@ python do_rootfs(){
|
||||
bb.utils.mkdirhier(sysconfdir)
|
||||
|
||||
execute_pre_post_process(d, d.getVar('ROOTFS_POSTPROCESS_COMMAND'))
|
||||
execute_pre_post_process(d, d.getVar("ROOTFS_POSTUNINSTALL_COMMAND"))
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +96,8 @@ SSTATE_SKIP_CREATION:task-image-complete = '1'
|
||||
do_image_complete[sstate-inputdirs] = "${IMGDEPLOYDIR}"
|
||||
do_image_complete[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}"
|
||||
do_image_complete[stamp-extra-info] = "${MACHINE_ARCH}"
|
||||
do_image_complete[sstate-plaindirs] += "${IMAGE_OUTPUT_MANIFEST_DIR}"
|
||||
do_image_complete[dirs] += "${IMAGE_OUTPUT_MANIFEST_DIR}"
|
||||
addtask do_image_complete after do_image before do_build
|
||||
|
||||
python do_image_complete_setscene () {
|
||||
@@ -140,5 +166,5 @@ python(){
|
||||
else:
|
||||
deps += " %s:%s" % (dep, task)
|
||||
return deps
|
||||
d.appendVarFlag('do_image', 'depends', extraimage_getdepends('do_populate_sysroot'))
|
||||
d.appendVarFlag('do_image', 'depends', extraimage_getdepends('do_populate_sysroot'))
|
||||
}
|
||||
|
||||
85
meta/classes-recipe/create-spdx-image-3.0.bbclass
Normal file
85
meta/classes-recipe/create-spdx-image-3.0.bbclass
Normal file
@@ -0,0 +1,85 @@
|
||||
#
|
||||
# Copyright OpenEmbedded Contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# SPDX image tasks
|
||||
|
||||
SPDX_ROOTFS_PACKAGES = "${SPDXDIR}/rootfs-packages.json"
|
||||
SPDXIMAGEDEPLOYDIR = "${SPDXDIR}/image-deploy"
|
||||
SPDXROOTFSDEPLOY = "${SPDXDIR}/rootfs-deploy"
|
||||
|
||||
python spdx_collect_rootfs_packages() {
|
||||
import json
|
||||
from pathlib import Path
|
||||
from oe.rootfs import image_list_installed_packages
|
||||
|
||||
root_packages_file = Path(d.getVar("SPDX_ROOTFS_PACKAGES"))
|
||||
|
||||
packages = image_list_installed_packages(d)
|
||||
if not packages:
|
||||
packages = {}
|
||||
|
||||
root_packages_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
with root_packages_file.open("w") as f:
|
||||
json.dump(packages, f)
|
||||
}
|
||||
ROOTFS_POSTUNINSTALL_COMMAND =+ "spdx_collect_rootfs_packages"
|
||||
|
||||
python do_create_rootfs_spdx() {
|
||||
import oe.spdx30_tasks
|
||||
oe.spdx30_tasks.create_rootfs_spdx(d)
|
||||
}
|
||||
addtask do_create_rootfs_spdx after do_rootfs before do_image
|
||||
SSTATETASKS += "do_create_rootfs_spdx"
|
||||
do_create_rootfs_spdx[sstate-inputdirs] = "${SPDXROOTFSDEPLOY}"
|
||||
do_create_rootfs_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}"
|
||||
do_create_rootfs_spdx[recrdeptask] += "do_create_spdx do_create_package_spdx"
|
||||
do_create_rootfs_spdx[cleandirs] += "${SPDXROOTFSDEPLOY}"
|
||||
do_create_rootfs_spdx[file-checksums] += "${SPDX3_LIB_DEP_FILES}"
|
||||
|
||||
python do_create_rootfs_spdx_setscene() {
|
||||
sstate_setscene(d)
|
||||
}
|
||||
addtask do_create_rootfs_spdx_setscene
|
||||
|
||||
python do_create_image_spdx() {
|
||||
import oe.spdx30_tasks
|
||||
oe.spdx30_tasks.create_image_spdx(d)
|
||||
}
|
||||
addtask do_create_image_spdx after do_image_complete do_create_rootfs_spdx before do_build
|
||||
SSTATETASKS += "do_create_image_spdx"
|
||||
SSTATE_SKIP_CREATION:task-create-image-spdx = "1"
|
||||
do_create_image_spdx[sstate-inputdirs] = "${SPDXIMAGEWORK}"
|
||||
do_create_image_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}"
|
||||
do_create_image_spdx[cleandirs] = "${SPDXIMAGEWORK}"
|
||||
do_create_image_spdx[dirs] = "${SPDXIMAGEWORK}"
|
||||
do_create_image_spdx[file-checksums] += "${SPDX3_LIB_DEP_FILES}"
|
||||
do_create_image_spdx[vardeps] += "\
|
||||
SPDX_IMAGE_PURPOSE \
|
||||
"
|
||||
|
||||
python do_create_image_spdx_setscene() {
|
||||
sstate_setscene(d)
|
||||
}
|
||||
addtask do_create_image_spdx_setscene
|
||||
|
||||
|
||||
python do_create_image_sbom_spdx() {
|
||||
import oe.spdx30_tasks
|
||||
oe.spdx30_tasks.create_image_sbom_spdx(d)
|
||||
}
|
||||
addtask do_create_image_sbom_spdx after do_create_rootfs_spdx do_create_image_spdx before do_build
|
||||
SSTATETASKS += "do_create_image_sbom_spdx"
|
||||
SSTATE_SKIP_CREATION:task-create-image-sbom = "1"
|
||||
do_create_image_sbom_spdx[sstate-inputdirs] = "${SPDXIMAGEDEPLOYDIR}"
|
||||
do_create_image_sbom_spdx[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}"
|
||||
do_create_image_sbom_spdx[stamp-extra-info] = "${MACHINE_ARCH}"
|
||||
do_create_image_sbom_spdx[cleandirs] = "${SPDXIMAGEDEPLOYDIR}"
|
||||
do_create_image_sbom_spdx[recrdeptask] += "do_create_spdx do_create_package_spdx"
|
||||
do_create_image_sbom_spdx[file-checksums] += "${SPDX3_LIB_DEP_FILES}"
|
||||
|
||||
python do_create_image_sbom_spdx_setscene() {
|
||||
sstate_setscene(d)
|
||||
}
|
||||
addtask do_create_image_sbom_spdx_setscene
|
||||
74
meta/classes-recipe/create-spdx-sdk-3.0.bbclass
Normal file
74
meta/classes-recipe/create-spdx-sdk-3.0.bbclass
Normal file
@@ -0,0 +1,74 @@
|
||||
#
|
||||
# Copyright OpenEmbedded Contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# SPDX SDK tasks
|
||||
|
||||
do_populate_sdk[recrdeptask] += "do_create_spdx do_create_package_spdx"
|
||||
do_populate_sdk[cleandirs] += "${SPDXSDKWORK}"
|
||||
do_populate_sdk[postfuncs] += "sdk_create_sbom"
|
||||
do_populate_sdk[file-checksums] += "${SPDX3_LIB_DEP_FILES}"
|
||||
POPULATE_SDK_POST_HOST_COMMAND:append:task-populate-sdk = " sdk_host_create_spdx"
|
||||
POPULATE_SDK_POST_TARGET_COMMAND:append:task-populate-sdk = " sdk_target_create_spdx"
|
||||
|
||||
do_populate_sdk_ext[recrdeptask] += "do_create_spdx do_create_package_spdx"
|
||||
do_populate_sdk_ext[cleandirs] += "${SPDXSDKEXTWORK}"
|
||||
do_populate_sdk_ext[postfuncs] += "sdk_ext_create_sbom"
|
||||
do_populate_sdk_ext[file-checksums] += "${SPDX3_LIB_DEP_FILES}"
|
||||
POPULATE_SDK_POST_HOST_COMMAND:append:task-populate-sdk-ext = " sdk_ext_host_create_spdx"
|
||||
POPULATE_SDK_POST_TARGET_COMMAND:append:task-populate-sdk-ext = " sdk_ext_target_create_spdx"
|
||||
|
||||
python sdk_host_create_spdx() {
|
||||
from pathlib import Path
|
||||
import oe.spdx30_tasks
|
||||
spdx_work_dir = Path(d.getVar('SPDXSDKWORK'))
|
||||
|
||||
oe.spdx30_tasks.sdk_create_spdx(d, "host", spdx_work_dir, d.getVar("TOOLCHAIN_OUTPUTNAME"))
|
||||
}
|
||||
|
||||
python sdk_target_create_spdx() {
|
||||
from pathlib import Path
|
||||
import oe.spdx30_tasks
|
||||
spdx_work_dir = Path(d.getVar('SPDXSDKWORK'))
|
||||
|
||||
oe.spdx30_tasks.sdk_create_spdx(d, "target", spdx_work_dir, d.getVar("TOOLCHAIN_OUTPUTNAME"))
|
||||
}
|
||||
|
||||
python sdk_ext_host_create_spdx() {
|
||||
from pathlib import Path
|
||||
import oe.spdx30_tasks
|
||||
spdx_work_dir = Path(d.getVar('SPDXSDKEXTWORK'))
|
||||
|
||||
# TODO: This doesn't seem to work
|
||||
oe.spdx30_tasks.sdk_create_spdx(d, "host", spdx_work_dir, d.getVar("TOOLCHAINEXT_OUTPUTNAME"))
|
||||
}
|
||||
|
||||
python sdk_ext_target_create_spdx() {
|
||||
from pathlib import Path
|
||||
import oe.spdx30_tasks
|
||||
spdx_work_dir = Path(d.getVar('SPDXSDKEXTWORK'))
|
||||
|
||||
# TODO: This doesn't seem to work
|
||||
oe.spdx30_tasks.sdk_create_spdx(d, "target", spdx_work_dir, d.getVar("TOOLCHAINEXT_OUTPUTNAME"))
|
||||
}
|
||||
|
||||
|
||||
python sdk_create_sbom() {
|
||||
from pathlib import Path
|
||||
import oe.spdx30_tasks
|
||||
sdk_deploydir = Path(d.getVar("SDKDEPLOYDIR"))
|
||||
spdx_work_dir = Path(d.getVar('SPDXSDKWORK'))
|
||||
|
||||
oe.spdx30_tasks.create_sdk_sbom(d, sdk_deploydir, spdx_work_dir, d.getVar("TOOLCHAIN_OUTPUTNAME"))
|
||||
}
|
||||
|
||||
python sdk_ext_create_sbom() {
|
||||
from pathlib import Path
|
||||
import oe.spdx30_tasks
|
||||
sdk_deploydir = Path(d.getVar("SDKEXTDEPLOYDIR"))
|
||||
spdx_work_dir = Path(d.getVar('SPDXSDKEXTWORK'))
|
||||
|
||||
oe.spdx30_tasks.create_sdk_sbom(d, sdk_deploydir, spdx_work_dir, d.getVar("TOOLCHAINEXT_OUTPUTNAME"))
|
||||
}
|
||||
|
||||
@@ -88,6 +88,11 @@ PACKAGE_INSTALL_ATTEMPTONLY ?= "${FEATURE_INSTALL_OPTIONAL}"
|
||||
|
||||
IMGDEPLOYDIR = "${WORKDIR}/deploy-${PN}-image-complete"
|
||||
|
||||
IMGMANIFESTDIR = "${WORKDIR}/image-task-manifest"
|
||||
|
||||
IMAGE_OUTPUT_MANIFEST_DIR = "${WORKDIR}/deploy-image-output-manifest"
|
||||
IMAGE_OUTPUT_MANIFEST = "${IMAGE_OUTPUT_MANIFEST_DIR}/manifest.json"
|
||||
|
||||
# Images are generally built explicitly, do not need to be part of world.
|
||||
EXCLUDE_FROM_WORLD = "1"
|
||||
|
||||
@@ -277,14 +282,28 @@ fakeroot python do_image () {
|
||||
execute_pre_post_process(d, pre_process_cmds)
|
||||
}
|
||||
do_image[dirs] = "${TOPDIR}"
|
||||
do_image[cleandirs] += "${IMGMANIFESTDIR}"
|
||||
addtask do_image after do_rootfs
|
||||
|
||||
fakeroot python do_image_complete () {
|
||||
from oe.utils import execute_pre_post_process
|
||||
from pathlib import Path
|
||||
import json
|
||||
|
||||
post_process_cmds = d.getVar("IMAGE_POSTPROCESS_COMMAND")
|
||||
|
||||
execute_pre_post_process(d, post_process_cmds)
|
||||
|
||||
image_manifest_dir = Path(d.getVar('IMGMANIFESTDIR'))
|
||||
|
||||
data = []
|
||||
|
||||
for manifest_path in image_manifest_dir.glob("*.json"):
|
||||
with manifest_path.open("r") as f:
|
||||
data.extend(json.load(f))
|
||||
|
||||
with open(d.getVar("IMAGE_OUTPUT_MANIFEST"), "w") as f:
|
||||
json.dump(data, f)
|
||||
}
|
||||
do_image_complete[dirs] = "${TOPDIR}"
|
||||
SSTATETASKS += "do_image_complete"
|
||||
@@ -292,6 +311,8 @@ SSTATE_SKIP_CREATION:task-image-complete = '1'
|
||||
do_image_complete[sstate-inputdirs] = "${IMGDEPLOYDIR}"
|
||||
do_image_complete[sstate-outputdirs] = "${DEPLOY_DIR_IMAGE}"
|
||||
do_image_complete[stamp-extra-info] = "${MACHINE_ARCH}"
|
||||
do_image_complete[sstate-plaindirs] += "${IMAGE_OUTPUT_MANIFEST_DIR}"
|
||||
do_image_complete[dirs] += "${IMAGE_OUTPUT_MANIFEST_DIR}"
|
||||
addtask do_image_complete after do_image before do_build
|
||||
python do_image_complete_setscene () {
|
||||
sstate_setscene(d)
|
||||
@@ -501,12 +522,14 @@ python () {
|
||||
d.setVar(task, '\n'.join(cmds))
|
||||
d.setVarFlag(task, 'func', '1')
|
||||
d.setVarFlag(task, 'fakeroot', '1')
|
||||
d.setVarFlag(task, 'imagetype', t)
|
||||
|
||||
d.appendVarFlag(task, 'prefuncs', ' ' + debug + ' set_image_size')
|
||||
d.prependVarFlag(task, 'postfuncs', 'create_symlinks ')
|
||||
d.appendVarFlag(task, 'subimages', ' ' + ' '.join(subimages))
|
||||
d.appendVarFlag(task, 'vardeps', ' ' + ' '.join(vardeps))
|
||||
d.appendVarFlag(task, 'vardepsexclude', ' DATETIME DATE ' + ' '.join(vardepsexclude))
|
||||
d.appendVarFlag(task, 'postfuncs', ' write_image_output_manifest')
|
||||
|
||||
bb.debug(2, "Adding task %s before %s, after %s" % (task, 'do_image_complete', after))
|
||||
bb.build.addtask(task, 'do_image_complete', after, d)
|
||||
@@ -604,6 +627,41 @@ python create_symlinks() {
|
||||
bb.note("Skipping symlink, source does not exist: %s -> %s" % (dst, src))
|
||||
}
|
||||
|
||||
python write_image_output_manifest() {
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
taskname = d.getVar("BB_CURRENTTASK")
|
||||
image_deploy_dir = Path(d.getVar('IMGDEPLOYDIR'))
|
||||
image_manifest_dir = Path(d.getVar('IMGMANIFESTDIR'))
|
||||
manifest_path = image_manifest_dir / ("do_" + d.getVar("BB_CURRENTTASK") + ".json")
|
||||
|
||||
image_name = d.getVar("IMAGE_NAME")
|
||||
image_basename = d.getVar("IMAGE_BASENAME")
|
||||
machine = d.getVar("MACHINE")
|
||||
|
||||
subimages = (d.getVarFlag("do_" + taskname, 'subimages', False) or "").split()
|
||||
imagetype = d.getVarFlag("do_" + taskname, 'imagetype', False)
|
||||
|
||||
data = {
|
||||
"taskname": taskname,
|
||||
"imagetype": imagetype,
|
||||
"images": []
|
||||
}
|
||||
|
||||
for type in subimages:
|
||||
image_filename = image_name + "." + type
|
||||
image_path = image_deploy_dir / image_filename
|
||||
if not image_path.exists():
|
||||
continue
|
||||
data["images"].append({
|
||||
"filename": image_filename,
|
||||
})
|
||||
|
||||
with manifest_path.open("w") as f:
|
||||
json.dump([data], f)
|
||||
}
|
||||
|
||||
MULTILIBRE_ALLOW_REP += "${base_bindir} ${base_sbindir} ${bindir} ${sbindir} ${libexecdir} ${sysconfdir} ${nonarch_base_libdir}/udev /lib/modules/[^/]*/modules.*"
|
||||
MULTILIB_CHECK_FILE = "${WORKDIR}/multilib_check.py"
|
||||
MULTILIB_TEMP_ROOTFS = "${WORKDIR}/multilib"
|
||||
|
||||
@@ -58,7 +58,7 @@ def write_license_files(d, license_manifest, pkg_dic, rootfs=True):
|
||||
import stat
|
||||
|
||||
bad_licenses = (d.getVar("INCOMPATIBLE_LICENSE") or "").split()
|
||||
bad_licenses = expand_wildcard_licenses(d, bad_licenses)
|
||||
bad_licenses = oe.license.expand_wildcard_licenses(d, bad_licenses)
|
||||
pkgarchs = d.getVar("SSTATE_ARCHS").split()
|
||||
pkgarchs.reverse()
|
||||
|
||||
@@ -66,17 +66,17 @@ def write_license_files(d, license_manifest, pkg_dic, rootfs=True):
|
||||
with open(license_manifest, "w") as license_file:
|
||||
for pkg in sorted(pkg_dic):
|
||||
remaining_bad_licenses = oe.license.apply_pkg_license_exception(pkg, bad_licenses, exceptions)
|
||||
incompatible_licenses = incompatible_pkg_license(d, remaining_bad_licenses, pkg_dic[pkg]["LICENSE"])
|
||||
incompatible_licenses = oe.license.incompatible_pkg_license(d, remaining_bad_licenses, pkg_dic[pkg]["LICENSE"])
|
||||
if incompatible_licenses:
|
||||
bb.fatal("Package %s cannot be installed into the image because it has incompatible license(s): %s" %(pkg, ' '.join(incompatible_licenses)))
|
||||
else:
|
||||
incompatible_licenses = incompatible_pkg_license(d, bad_licenses, pkg_dic[pkg]["LICENSE"])
|
||||
incompatible_licenses = oe.license.incompatible_pkg_license(d, bad_licenses, pkg_dic[pkg]["LICENSE"])
|
||||
if incompatible_licenses:
|
||||
oe.qa.handle_error('license-incompatible', "Including %s with incompatible license(s) %s into the image, because it has been allowed by exception list." %(pkg, ' '.join(incompatible_licenses)), d)
|
||||
try:
|
||||
(pkg_dic[pkg]["LICENSE"], pkg_dic[pkg]["LICENSES"]) = \
|
||||
oe.license.manifest_licenses(pkg_dic[pkg]["LICENSE"],
|
||||
remaining_bad_licenses, canonical_license, d)
|
||||
remaining_bad_licenses, oe.license.canonical_license, d)
|
||||
except oe.license.LicenseError as exc:
|
||||
bb.fatal('%s: %s' % (d.getVar('P'), exc))
|
||||
|
||||
@@ -144,7 +144,7 @@ def write_license_files(d, license_manifest, pkg_dic, rootfs=True):
|
||||
if not os.path.exists(pkg_license_dir ):
|
||||
bb.fatal("Couldn't find license information for dependency %s" % pkg)
|
||||
|
||||
pkg_manifest_licenses = [canonical_license(d, lic) \
|
||||
pkg_manifest_licenses = [oe.license.canonical_license(d, lic) \
|
||||
for lic in pkg_dic[pkg]["LICENSES"]]
|
||||
|
||||
licenses = os.listdir(pkg_license_dir)
|
||||
@@ -153,7 +153,7 @@ def write_license_files(d, license_manifest, pkg_dic, rootfs=True):
|
||||
pkg_rootfs_license = os.path.join(pkg_rootfs_license_dir, lic)
|
||||
|
||||
if re.match(r"^generic_.*$", lic):
|
||||
generic_lic = canonical_license(d,
|
||||
generic_lic = oe.license.canonical_license(d,
|
||||
re.search(r"^generic_(.*)$", lic).group(1))
|
||||
|
||||
# Do not copy generic license into package if isn't
|
||||
@@ -176,7 +176,7 @@ def write_license_files(d, license_manifest, pkg_dic, rootfs=True):
|
||||
if not os.path.exists(pkg_rootfs_license):
|
||||
os.symlink(os.path.join('..', generic_lic_file), pkg_rootfs_license)
|
||||
else:
|
||||
if (oe.license.license_ok(canonical_license(d,
|
||||
if (oe.license.license_ok(oe.license.canonical_license(d,
|
||||
lic), bad_licenses) == False or
|
||||
os.path.exists(pkg_rootfs_license)):
|
||||
continue
|
||||
|
||||
13
meta/classes-recipe/nospdx.bbclass
Normal file
13
meta/classes-recipe/nospdx.bbclass
Normal file
@@ -0,0 +1,13 @@
|
||||
#
|
||||
# Copyright OpenEmbedded Contributors
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
#
|
||||
|
||||
deltask do_collect_spdx_deps
|
||||
deltask do_create_spdx
|
||||
deltask do_create_spdx_runtime
|
||||
deltask do_create_package_spdx
|
||||
deltask do_create_rootfs_spdx
|
||||
deltask do_create_image_spdx
|
||||
deltask do_create_image_sbom
|
||||
@@ -4,7 +4,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
|
||||
DEPLOY_DIR_SPDX ??= "${DEPLOY_DIR}/spdx"
|
||||
DEPLOY_DIR_SPDX ??= "${DEPLOY_DIR}/spdx/${SPDX_VERSION}"
|
||||
|
||||
# The product name that the CVE database uses. Defaults to BPN, but may need to
|
||||
# be overriden per recipe (for example tiff.bb sets CVE_PRODUCT=libtiff).
|
||||
@@ -1044,52 +1044,53 @@ def combine_spdx(d, rootfs_name, rootfs_deploydir, rootfs_spdxid, packages, spdx
|
||||
|
||||
doc.packages.append(image)
|
||||
|
||||
for name in sorted(packages.keys()):
|
||||
if name not in providers:
|
||||
bb.fatal("Unable to find SPDX provider for '%s'" % name)
|
||||
if packages:
|
||||
for name in sorted(packages.keys()):
|
||||
if name not in providers:
|
||||
bb.fatal("Unable to find SPDX provider for '%s'" % name)
|
||||
|
||||
pkg_name, pkg_hashfn = providers[name]
|
||||
pkg_name, pkg_hashfn = providers[name]
|
||||
|
||||
pkg_spdx_path = oe.sbom.doc_find_by_hashfn(deploy_dir_spdx, package_archs, pkg_name, pkg_hashfn)
|
||||
if not pkg_spdx_path:
|
||||
bb.fatal("No SPDX file found for package %s, %s" % (pkg_name, pkg_hashfn))
|
||||
pkg_spdx_path = oe.sbom.doc_find_by_hashfn(deploy_dir_spdx, package_archs, pkg_name, pkg_hashfn)
|
||||
if not pkg_spdx_path:
|
||||
bb.fatal("No SPDX file found for package %s, %s" % (pkg_name, pkg_hashfn))
|
||||
|
||||
pkg_doc, pkg_doc_sha1 = oe.sbom.read_doc(pkg_spdx_path)
|
||||
pkg_doc, pkg_doc_sha1 = oe.sbom.read_doc(pkg_spdx_path)
|
||||
|
||||
for p in pkg_doc.packages:
|
||||
if p.name == name:
|
||||
pkg_ref = oe.spdx.SPDXExternalDocumentRef()
|
||||
pkg_ref.externalDocumentId = "DocumentRef-%s" % pkg_doc.name
|
||||
pkg_ref.spdxDocument = pkg_doc.documentNamespace
|
||||
pkg_ref.checksum.algorithm = "SHA1"
|
||||
pkg_ref.checksum.checksumValue = pkg_doc_sha1
|
||||
for p in pkg_doc.packages:
|
||||
if p.name == name:
|
||||
pkg_ref = oe.spdx.SPDXExternalDocumentRef()
|
||||
pkg_ref.externalDocumentId = "DocumentRef-%s" % pkg_doc.name
|
||||
pkg_ref.spdxDocument = pkg_doc.documentNamespace
|
||||
pkg_ref.checksum.algorithm = "SHA1"
|
||||
pkg_ref.checksum.checksumValue = pkg_doc_sha1
|
||||
|
||||
doc.externalDocumentRefs.append(pkg_ref)
|
||||
doc.add_relationship(image, "CONTAINS", "%s:%s" % (pkg_ref.externalDocumentId, p.SPDXID))
|
||||
break
|
||||
else:
|
||||
bb.fatal("Unable to find package with name '%s' in SPDX file %s" % (name, pkg_spdx_path))
|
||||
doc.externalDocumentRefs.append(pkg_ref)
|
||||
doc.add_relationship(image, "CONTAINS", "%s:%s" % (pkg_ref.externalDocumentId, p.SPDXID))
|
||||
break
|
||||
else:
|
||||
bb.fatal("Unable to find package with name '%s' in SPDX file %s" % (name, pkg_spdx_path))
|
||||
|
||||
runtime_spdx_path = oe.sbom.doc_find_by_hashfn(deploy_dir_spdx, package_archs, "runtime-" + name, pkg_hashfn)
|
||||
if not runtime_spdx_path:
|
||||
bb.fatal("No runtime SPDX document found for %s, %s" % (name, pkg_hashfn))
|
||||
runtime_spdx_path = oe.sbom.doc_find_by_hashfn(deploy_dir_spdx, package_archs, "runtime-" + name, pkg_hashfn)
|
||||
if not runtime_spdx_path:
|
||||
bb.fatal("No runtime SPDX document found for %s, %s" % (name, pkg_hashfn))
|
||||
|
||||
runtime_doc, runtime_doc_sha1 = oe.sbom.read_doc(runtime_spdx_path)
|
||||
runtime_doc, runtime_doc_sha1 = oe.sbom.read_doc(runtime_spdx_path)
|
||||
|
||||
runtime_ref = oe.spdx.SPDXExternalDocumentRef()
|
||||
runtime_ref.externalDocumentId = "DocumentRef-%s" % runtime_doc.name
|
||||
runtime_ref.spdxDocument = runtime_doc.documentNamespace
|
||||
runtime_ref.checksum.algorithm = "SHA1"
|
||||
runtime_ref.checksum.checksumValue = runtime_doc_sha1
|
||||
runtime_ref = oe.spdx.SPDXExternalDocumentRef()
|
||||
runtime_ref.externalDocumentId = "DocumentRef-%s" % runtime_doc.name
|
||||
runtime_ref.spdxDocument = runtime_doc.documentNamespace
|
||||
runtime_ref.checksum.algorithm = "SHA1"
|
||||
runtime_ref.checksum.checksumValue = runtime_doc_sha1
|
||||
|
||||
# "OTHER" isn't ideal here, but I can't find a relationship that makes sense
|
||||
doc.externalDocumentRefs.append(runtime_ref)
|
||||
doc.add_relationship(
|
||||
image,
|
||||
"OTHER",
|
||||
"%s:%s" % (runtime_ref.externalDocumentId, runtime_doc.SPDXID),
|
||||
comment="Runtime dependencies for %s" % name
|
||||
)
|
||||
# "OTHER" isn't ideal here, but I can't find a relationship that makes sense
|
||||
doc.externalDocumentRefs.append(runtime_ref)
|
||||
doc.add_relationship(
|
||||
image,
|
||||
"OTHER",
|
||||
"%s:%s" % (runtime_ref.externalDocumentId, runtime_doc.SPDXID),
|
||||
comment="Runtime dependencies for %s" % name
|
||||
)
|
||||
bb.utils.mkdirhier(spdx_workdir)
|
||||
image_spdx_path = spdx_workdir / (rootfs_name + ".spdx.json")
|
||||
|
||||
|
||||
200
meta/classes/create-spdx-3.0.bbclass
Normal file
200
meta/classes/create-spdx-3.0.bbclass
Normal file
@@ -0,0 +1,200 @@
|
||||
#
|
||||
# Copyright OpenEmbedded Contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
|
||||
inherit spdx-common
|
||||
|
||||
SPDX_VERSION = "3.0.1"
|
||||
|
||||
# The list of SPDX profiles generated documents will conform to
|
||||
SPDX_PROFILES ?= "core build software simpleLicensing security"
|
||||
|
||||
SPDX_INCLUDE_BUILD_VARIABLES ??= "0"
|
||||
SPDX_INCLUDE_BUILD_VARIABLES[doc] = "If set to '1', the bitbake variables for a \
|
||||
recipe will be included in the Build object. This will most likely result \
|
||||
in non-reproducible SPDX output"
|
||||
|
||||
SPDX_INCLUDE_BITBAKE_PARENT_BUILD ??= "0"
|
||||
SPDX_INCLUDE_BITBAKE_PARENT_BUILD[doc] = "Report the parent invocation of bitbake \
|
||||
for each Build object. This allows you to know who invoked bitbake to perform \
|
||||
a build, but will result in non-reproducible SPDX output."
|
||||
|
||||
SPDX_PACKAGE_ADDITIONAL_PURPOSE ?= ""
|
||||
SPDX_PACKAGE_ADDITIONAL_PURPOSE[doc] = "The list of additional purposes to assign to \
|
||||
the generated packages for a recipe. The primary purpose is always `install`. \
|
||||
Packages overrides are allowed to override the additional purposes for \
|
||||
individual packages."
|
||||
|
||||
SPDX_IMAGE_PURPOSE ?= "filesystemImage"
|
||||
SPDX_IMAGE_PURPOSE[doc] = "The list of purposes to assign to the generated images. \
|
||||
The first listed item will be the Primary Purpose and all additional items will \
|
||||
be added as additional purposes"
|
||||
|
||||
SPDX_SDK_PURPOSE ?= "install"
|
||||
SPDX_SDK_PURPOSE[doc] = "The list of purposes to assign to the generate SDK installer. \
|
||||
The first listed item will be the Primary Purpose and all additional items will \
|
||||
be added as additional purposes"
|
||||
|
||||
SPDX_INCLUDE_VEX ??= "current"
|
||||
SPDX_INCLUDE_VEX[doc] = "Controls what VEX information is in the output. Set to \
|
||||
'none' to disable all VEX data. Set to 'current' to only include VEX data \
|
||||
for vulnerabilities not already fixed in the upstream source code \
|
||||
(recommended). Set to 'all' to get all known historical vulnerabilities, \
|
||||
including those already fixed upstream (warning: This can be large and \
|
||||
slow)."
|
||||
|
||||
SPDX_INCLUDE_TIMESTAMPS ?= "0"
|
||||
SPDX_INCLUDE_TIMESTAMPS[doc] = "Include time stamps in SPDX output. This is \
|
||||
useful if you want to know when artifacts were produced and when builds \
|
||||
occurred, but will result in non-reproducible SPDX output"
|
||||
|
||||
SPDX_IMPORTS ??= ""
|
||||
SPDX_IMPORTS[doc] = "SPDX_IMPORTS is the base variable that describes how to \
|
||||
reference external SPDX ids. Each import is defined as a key in this \
|
||||
variable with a suffix to describe to as a suffix to look up more \
|
||||
information about the import. Each key can have the following variables: \
|
||||
SPDX_IMPORTS_<key>_spdxid: The Fully qualified SPDX ID of the object \
|
||||
SPDX_IMPORTS_<key>_uri: The URI where the SPDX Document that contains \
|
||||
the external object can be found. Optional but recommended \
|
||||
SPDX_IMPORTS_<key>_hash_<hash>: The Checksum of the SPDX Document that \
|
||||
contains the External ID. <hash> must be one the valid SPDX hashing \
|
||||
algorithms, as described by the HashAlgorithm vocabulary in the\
|
||||
SPDX 3 spec. Optional but recommended"
|
||||
|
||||
# Agents
|
||||
# Bitbake variables can be used to describe an SPDX Agent that may be used
|
||||
# during the build. An Agent is specified using a set of variables which all
|
||||
# start with some common base name:
|
||||
#
|
||||
# <BASE>_name: The name of the Agent (required)
|
||||
# <BASE>_type: The type of Agent. Must be one of "person", "organization",
|
||||
# "software", or "agent" (the default if not specified)
|
||||
# <BASE>_comment: The comment for the Agent (optional)
|
||||
# <BASE>_id_<ID>: And External Identifier for the Agent. <ID> must be a valid
|
||||
# ExternalIdentifierType from the SPDX 3 spec. Commonly, an E-mail address
|
||||
# can be specified with <BASE>_id_email
|
||||
#
|
||||
# Alternatively, an Agent can be an external reference by referencing a key
|
||||
# in SPDX_IMPORTS like so:
|
||||
#
|
||||
# <BASE>_import = "<key>"
|
||||
#
|
||||
# Finally, the same agent described by another set of agent variables can be
|
||||
# referenced by specifying the basename of the variable that should be
|
||||
# referenced:
|
||||
#
|
||||
# SPDX_PACKAGE_SUPPLIER_ref = "SPDX_AUTHORS_openembedded"
|
||||
|
||||
SPDX_AUTHORS ??= "openembedded"
|
||||
SPDX_AUTHORS[doc] = "A space separated list of the document authors. Each item \
|
||||
is used to name a base variable like SPDX_AUTHORS_<AUTHOR> that \
|
||||
describes the author."
|
||||
|
||||
SPDX_AUTHORS_openembedded_name = "OpenEmbedded"
|
||||
SPDX_AUTHORS_openembedded_type = "organization"
|
||||
|
||||
SPDX_BUILD_HOST[doc] = "The base variable name to describe the build host on \
|
||||
which a build is running. Must be an SPDX_IMPORTS key. Requires \
|
||||
SPDX_INCLUDE_BITBAKE_PARENT_BUILD. NOTE: Setting this will result in \
|
||||
non-reproducible SPDX output"
|
||||
|
||||
SPDX_INVOKED_BY[doc] = "The base variable name to describe the Agent that \
|
||||
invoked the build, which builds will link to if specified. Requires \
|
||||
SPDX_INCLUDE_BITBAKE_PARENT_BUILD. NOTE: Setting this will likely result in \
|
||||
non-reproducible SPDX output"
|
||||
|
||||
SPDX_ON_BEHALF_OF[doc] = "The base variable name to describe the Agent on who's \
|
||||
behalf the invoking Agent (SPDX_INVOKED_BY) is running the build. Requires \
|
||||
SPDX_INCLUDE_BITBAKE_PARENT_BUILD. NOTE: Setting this will likely result in \
|
||||
non-reproducible SPDX output"
|
||||
|
||||
SPDX_PACKAGE_SUPPLIER[doc] = "The base variable name to describe the Agent who \
|
||||
is supplying artifacts produced by the build"
|
||||
|
||||
SPDX_PACKAGE_VERSION ??= "${PV}"
|
||||
SPDX_PACKAGE_VERSION[doc] = "The version of a package, software_packageVersion \
|
||||
in software_Package"
|
||||
|
||||
IMAGE_CLASSES:append = " create-spdx-image-3.0"
|
||||
SDK_CLASSES += "create-spdx-sdk-3.0"
|
||||
|
||||
oe.spdx30_tasks.set_timestamp_now[vardepsexclude] = "SPDX_INCLUDE_TIMESTAMPS"
|
||||
oe.spdx30_tasks.get_package_sources_from_debug[vardepsexclude] += "STAGING_KERNEL_DIR"
|
||||
oe.spdx30_tasks.collect_dep_objsets[vardepsexclude] = "SPDX_MULTILIB_SSTATE_ARCHS"
|
||||
|
||||
|
||||
# SPDX library code makes heavy use of classes, which bitbake cannot easily
|
||||
# parse out dependencies. As such, the library code files that make use of
|
||||
# classes are explicitly added as file checksum dependencies.
|
||||
SPDX3_LIB_DEP_FILES = "\
|
||||
${COREBASE}/meta/lib/oe/sbom30.py:True \
|
||||
${COREBASE}/meta/lib/oe/spdx30.py:True \
|
||||
"
|
||||
|
||||
python do_create_spdx() {
|
||||
import oe.spdx30_tasks
|
||||
oe.spdx30_tasks.create_spdx(d)
|
||||
}
|
||||
do_create_spdx[vardeps] += "\
|
||||
SPDX_INCLUDE_BITBAKE_PARENT_BUILD \
|
||||
SPDX_PACKAGE_ADDITIONAL_PURPOSE \
|
||||
SPDX_PROFILES \
|
||||
SPDX_NAMESPACE_PREFIX \
|
||||
SPDX_UUID_NAMESPACE \
|
||||
"
|
||||
|
||||
addtask do_create_spdx after \
|
||||
do_collect_spdx_deps \
|
||||
do_deploy_source_date_epoch \
|
||||
do_populate_sysroot do_package do_packagedata \
|
||||
before do_populate_sdk do_populate_sdk_ext do_build do_rm_work
|
||||
|
||||
SSTATETASKS += "do_create_spdx"
|
||||
do_create_spdx[sstate-inputdirs] = "${SPDXDEPLOY}"
|
||||
do_create_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}"
|
||||
do_create_spdx[file-checksums] += "${SPDX3_LIB_DEP_FILES}"
|
||||
|
||||
python do_create_spdx_setscene () {
|
||||
sstate_setscene(d)
|
||||
}
|
||||
addtask do_create_spdx_setscene
|
||||
|
||||
do_create_spdx[dirs] = "${SPDXWORK}"
|
||||
do_create_spdx[cleandirs] = "${SPDXDEPLOY} ${SPDXWORK}"
|
||||
do_create_spdx[depends] += " \
|
||||
${PATCHDEPENDENCY} \
|
||||
${@create_spdx_source_deps(d)} \
|
||||
"
|
||||
|
||||
python do_create_package_spdx() {
|
||||
import oe.spdx30_tasks
|
||||
oe.spdx30_tasks.create_package_spdx(d)
|
||||
}
|
||||
oe.spdx30_tasks.create_package_spdx[vardepsexclude] = "OVERRIDES"
|
||||
|
||||
addtask do_create_package_spdx after do_create_spdx before do_build do_rm_work
|
||||
SSTATETASKS += "do_create_package_spdx"
|
||||
do_create_package_spdx[sstate-inputdirs] = "${SPDXRUNTIMEDEPLOY}"
|
||||
do_create_package_spdx[sstate-outputdirs] = "${DEPLOY_DIR_SPDX}"
|
||||
do_create_package_spdx[file-checksums] += "${SPDX3_LIB_DEP_FILES}"
|
||||
|
||||
python do_create_package_spdx_setscene () {
|
||||
sstate_setscene(d)
|
||||
}
|
||||
addtask do_create_package_spdx_setscene
|
||||
|
||||
do_create_package_spdx[dirs] = "${SPDXRUNTIMEDEPLOY}"
|
||||
do_create_package_spdx[cleandirs] = "${SPDXRUNTIMEDEPLOY}"
|
||||
do_create_package_spdx[rdeptask] = "do_create_spdx"
|
||||
|
||||
python spdx30_build_started_handler () {
|
||||
import oe.spdx30_tasks
|
||||
d = e.data.createCopy()
|
||||
oe.spdx30_tasks.write_bitbake_spdx(d)
|
||||
}
|
||||
|
||||
addhandler spdx30_build_started_handler
|
||||
spdx30_build_started_handler[eventmask] = "bb.event.BuildStarted"
|
||||
|
||||
99
meta/classes/spdx-common.bbclass
Normal file
99
meta/classes/spdx-common.bbclass
Normal file
@@ -0,0 +1,99 @@
|
||||
#
|
||||
# Copyright OpenEmbedded Contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
|
||||
SPDX_VERSION ??= ""
|
||||
DEPLOY_DIR_SPDX ??= "${DEPLOY_DIR}/spdx/${SPDX_VERSION}"
|
||||
|
||||
# The product name that the CVE database uses. Defaults to BPN, but may need to
|
||||
# be overriden per recipe (for example tiff.bb sets CVE_PRODUCT=libtiff).
|
||||
CVE_PRODUCT ??= "${BPN}"
|
||||
CVE_VERSION ??= "${PV}"
|
||||
|
||||
SPDXDIR ??= "${WORKDIR}/spdx/${SPDX_VERSION}"
|
||||
SPDXDEPLOY = "${SPDXDIR}/deploy"
|
||||
SPDXWORK = "${SPDXDIR}/work"
|
||||
SPDXIMAGEWORK = "${SPDXDIR}/image-work"
|
||||
SPDXSDKWORK = "${SPDXDIR}/sdk-work"
|
||||
SPDXSDKEXTWORK = "${SPDXDIR}/sdk-ext-work"
|
||||
SPDXDEPS = "${SPDXDIR}/deps.json"
|
||||
|
||||
SPDX_TOOL_NAME ??= "oe-spdx-creator"
|
||||
SPDX_TOOL_VERSION ??= "1.0"
|
||||
|
||||
SPDXRUNTIMEDEPLOY = "${SPDXDIR}/runtime-deploy"
|
||||
|
||||
SPDX_INCLUDE_SOURCES ??= "0"
|
||||
|
||||
SPDX_UUID_NAMESPACE ??= "sbom.openembedded.org"
|
||||
SPDX_NAMESPACE_PREFIX ??= "http://spdx.org/spdxdocs"
|
||||
SPDX_PRETTY ??= "0"
|
||||
|
||||
SPDX_LICENSES ??= "${COREBASE}/meta/files/spdx-licenses.json"
|
||||
|
||||
SPDX_CUSTOM_ANNOTATION_VARS ??= ""
|
||||
|
||||
SPDX_MULTILIB_SSTATE_ARCHS ??= "${SSTATE_ARCHS}"
|
||||
|
||||
def create_spdx_source_deps(d):
|
||||
import oe.spdx_common
|
||||
|
||||
deps = []
|
||||
if d.getVar("SPDX_INCLUDE_SOURCES") == "1":
|
||||
pn = d.getVar('PN')
|
||||
# do_unpack is a hack for now; we only need it to get the
|
||||
# dependencies do_unpack already has so we can extract the source
|
||||
# ourselves
|
||||
if oe.spdx_common.has_task(d, "do_unpack"):
|
||||
deps.append("%s:do_unpack" % pn)
|
||||
|
||||
if oe.spdx_common.is_work_shared_spdx(d) and \
|
||||
oe.spdx_common.process_sources(d):
|
||||
# For kernel source code
|
||||
if oe.spdx_common.has_task(d, "do_shared_workdir"):
|
||||
deps.append("%s:do_shared_workdir" % pn)
|
||||
elif d.getVar('S') == d.getVar('STAGING_KERNEL_DIR'):
|
||||
deps.append("virtual/kernel:do_shared_workdir")
|
||||
|
||||
# For gcc-source-${PV} source code
|
||||
if oe.spdx_common.has_task(d, "do_preconfigure"):
|
||||
deps.append("%s:do_preconfigure" % pn)
|
||||
elif oe.spdx_common.has_task(d, "do_patch"):
|
||||
deps.append("%s:do_patch" % pn)
|
||||
# For gcc-cross-x86_64 source code
|
||||
elif oe.spdx_common.has_task(d, "do_configure"):
|
||||
deps.append("%s:do_configure" % pn)
|
||||
|
||||
return " ".join(deps)
|
||||
|
||||
|
||||
python do_collect_spdx_deps() {
|
||||
# This task calculates the build time dependencies of the recipe, and is
|
||||
# required because while a task can deptask on itself, those dependencies
|
||||
# do not show up in BB_TASKDEPDATA. To work around that, this task does the
|
||||
# deptask on do_create_spdx and writes out the dependencies it finds, then
|
||||
# do_create_spdx reads in the found dependencies when writing the actual
|
||||
# SPDX document
|
||||
import json
|
||||
import oe.spdx_common
|
||||
from pathlib import Path
|
||||
|
||||
spdx_deps_file = Path(d.getVar("SPDXDEPS"))
|
||||
|
||||
deps = oe.spdx_common.collect_direct_deps(d, "do_create_spdx")
|
||||
|
||||
with spdx_deps_file.open("w") as f:
|
||||
json.dump(deps, f)
|
||||
}
|
||||
# NOTE: depending on do_unpack is a hack that is necessary to get it's dependencies for archive the source
|
||||
addtask do_collect_spdx_deps after do_unpack
|
||||
do_collect_spdx_deps[depends] += "${PATCHDEPENDENCY}"
|
||||
do_collect_spdx_deps[deptask] = "do_create_spdx"
|
||||
do_collect_spdx_deps[dirs] = "${SPDXDIR}"
|
||||
|
||||
oe.spdx_common.collect_direct_deps[vardepsexclude] += "BB_TASKDEPDATA"
|
||||
oe.spdx_common.collect_direct_deps[vardeps] += "DEPENDS"
|
||||
oe.spdx_common.collect_package_providers[vardepsexclude] += "BB_TASKDEPDATA"
|
||||
oe.spdx_common.get_patched_src[vardepsexclude] += "STAGING_KERNEL_DIR"
|
||||
@@ -9,4 +9,4 @@ __path__ = extend_path(__path__, __name__)
|
||||
|
||||
BBIMPORTS = ["data", "path", "utils", "types", "package", "packagedata", \
|
||||
"packagegroup", "sstatesig", "lsb", "cachedpath", "license", \
|
||||
"qa", "reproducible", "rust", "buildcfg", "go"]
|
||||
"qa", "reproducible", "rust", "buildcfg", "go", "spdx30_tasks", "spdx_common"]
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import ast
|
||||
import re
|
||||
import oe.qa
|
||||
from fnmatch import fnmatchcase as fnmatch
|
||||
|
||||
def license_ok(license, dont_want_licenses):
|
||||
@@ -259,3 +260,205 @@ def apply_pkg_license_exception(pkg, bad_licenses, exceptions):
|
||||
"""Return remaining bad licenses after removing any package exceptions"""
|
||||
|
||||
return [lic for lic in bad_licenses if pkg + ':' + lic not in exceptions]
|
||||
|
||||
def return_spdx(d, license):
|
||||
"""
|
||||
This function returns the spdx mapping of a license if it exists.
|
||||
"""
|
||||
return d.getVarFlag('SPDXLICENSEMAP', license)
|
||||
|
||||
def canonical_license(d, license):
|
||||
"""
|
||||
Return the canonical (SPDX) form of the license if available (so GPLv3
|
||||
becomes GPL-3.0-only) or the passed license if there is no canonical form.
|
||||
"""
|
||||
return d.getVarFlag('SPDXLICENSEMAP', license) or license
|
||||
|
||||
def expand_wildcard_licenses(d, wildcard_licenses):
|
||||
"""
|
||||
There are some common wildcard values users may want to use. Support them
|
||||
here.
|
||||
"""
|
||||
licenses = set(wildcard_licenses)
|
||||
mapping = {
|
||||
"AGPL-3.0*" : ["AGPL-3.0-only", "AGPL-3.0-or-later"],
|
||||
"GPL-3.0*" : ["GPL-3.0-only", "GPL-3.0-or-later"],
|
||||
"LGPL-3.0*" : ["LGPL-3.0-only", "LGPL-3.0-or-later"],
|
||||
}
|
||||
for k in mapping:
|
||||
if k in wildcard_licenses:
|
||||
licenses.remove(k)
|
||||
for item in mapping[k]:
|
||||
licenses.add(item)
|
||||
|
||||
for l in licenses:
|
||||
if l in obsolete_license_list():
|
||||
bb.fatal("Error, %s is an obsolete license, please use an SPDX reference in INCOMPATIBLE_LICENSE" % l)
|
||||
if "*" in l:
|
||||
bb.fatal("Error, %s is an invalid license wildcard entry" % l)
|
||||
|
||||
return list(licenses)
|
||||
|
||||
def incompatible_license_contains(license, truevalue, falsevalue, d):
|
||||
license = canonical_license(d, license)
|
||||
bad_licenses = (d.getVar('INCOMPATIBLE_LICENSE') or "").split()
|
||||
bad_licenses = expand_wildcard_licenses(d, bad_licenses)
|
||||
return truevalue if license in bad_licenses else falsevalue
|
||||
|
||||
def incompatible_pkg_license(d, dont_want_licenses, license):
|
||||
# Handles an "or" or two license sets provided by
|
||||
# flattened_licenses(), pick one that works if possible.
|
||||
def choose_lic_set(a, b):
|
||||
return a if all(license_ok(canonical_license(d, lic),
|
||||
dont_want_licenses) for lic in a) else b
|
||||
|
||||
try:
|
||||
licenses = flattened_licenses(license, choose_lic_set)
|
||||
except LicenseError as exc:
|
||||
bb.fatal('%s: %s' % (d.getVar('P'), exc))
|
||||
|
||||
incompatible_lic = []
|
||||
for l in licenses:
|
||||
license = canonical_license(d, l)
|
||||
if not license_ok(license, dont_want_licenses):
|
||||
incompatible_lic.append(license)
|
||||
|
||||
return sorted(incompatible_lic)
|
||||
|
||||
def incompatible_license(d, dont_want_licenses, package=None):
|
||||
"""
|
||||
This function checks if a recipe has only incompatible licenses. It also
|
||||
take into consideration 'or' operand. dont_want_licenses should be passed
|
||||
as canonical (SPDX) names.
|
||||
"""
|
||||
license = d.getVar("LICENSE:%s" % package) if package else None
|
||||
if not license:
|
||||
license = d.getVar('LICENSE')
|
||||
|
||||
return incompatible_pkg_license(d, dont_want_licenses, license)
|
||||
|
||||
def check_license_flags(d):
|
||||
"""
|
||||
This function checks if a recipe has any LICENSE_FLAGS that
|
||||
aren't acceptable.
|
||||
|
||||
If it does, it returns the all LICENSE_FLAGS missing from the list
|
||||
of acceptable license flags, or all of the LICENSE_FLAGS if there
|
||||
is no list of acceptable flags.
|
||||
|
||||
If everything is is acceptable, it returns None.
|
||||
"""
|
||||
|
||||
def license_flag_matches(flag, acceptlist, pn):
|
||||
"""
|
||||
Return True if flag matches something in acceptlist, None if not.
|
||||
|
||||
Before we test a flag against the acceptlist, we append _${PN}
|
||||
to it. We then try to match that string against the
|
||||
acceptlist. This covers the normal case, where we expect
|
||||
LICENSE_FLAGS to be a simple string like 'commercial', which
|
||||
the user typically matches exactly in the acceptlist by
|
||||
explicitly appending the package name e.g 'commercial_foo'.
|
||||
If we fail the match however, we then split the flag across
|
||||
'_' and append each fragment and test until we either match or
|
||||
run out of fragments.
|
||||
"""
|
||||
flag_pn = ("%s_%s" % (flag, pn))
|
||||
for candidate in acceptlist:
|
||||
if flag_pn == candidate:
|
||||
return True
|
||||
|
||||
flag_cur = ""
|
||||
flagments = flag_pn.split("_")
|
||||
flagments.pop() # we've already tested the full string
|
||||
for flagment in flagments:
|
||||
if flag_cur:
|
||||
flag_cur += "_"
|
||||
flag_cur += flagment
|
||||
for candidate in acceptlist:
|
||||
if flag_cur == candidate:
|
||||
return True
|
||||
return False
|
||||
|
||||
def all_license_flags_match(license_flags, acceptlist):
|
||||
""" Return all unmatched flags, None if all flags match """
|
||||
pn = d.getVar('PN')
|
||||
split_acceptlist = acceptlist.split()
|
||||
flags = []
|
||||
for flag in license_flags.split():
|
||||
if not license_flag_matches(flag, split_acceptlist, pn):
|
||||
flags.append(flag)
|
||||
return flags if flags else None
|
||||
|
||||
license_flags = d.getVar('LICENSE_FLAGS')
|
||||
if license_flags:
|
||||
acceptlist = d.getVar('LICENSE_FLAGS_ACCEPTED')
|
||||
if not acceptlist:
|
||||
return license_flags.split()
|
||||
unmatched_flags = all_license_flags_match(license_flags, acceptlist)
|
||||
if unmatched_flags:
|
||||
return unmatched_flags
|
||||
return None
|
||||
|
||||
def check_license_format(d):
|
||||
"""
|
||||
This function checks if LICENSE is well defined,
|
||||
Validate operators in LICENSES.
|
||||
No spaces are allowed between LICENSES.
|
||||
"""
|
||||
pn = d.getVar('PN')
|
||||
licenses = d.getVar('LICENSE')
|
||||
|
||||
elements = list(filter(lambda x: x.strip(), license_operator.split(licenses)))
|
||||
for pos, element in enumerate(elements):
|
||||
if license_pattern.match(element):
|
||||
if pos > 0 and license_pattern.match(elements[pos - 1]):
|
||||
oe.qa.handle_error('license-format',
|
||||
'%s: LICENSE value "%s" has an invalid format - license names ' \
|
||||
'must be separated by the following characters to indicate ' \
|
||||
'the license selection: %s' %
|
||||
(pn, licenses, license_operator_chars), d)
|
||||
elif not license_operator.match(element):
|
||||
oe.qa.handle_error('license-format',
|
||||
'%s: LICENSE value "%s" has an invalid separator "%s" that is not ' \
|
||||
'in the valid list of separators (%s)' %
|
||||
(pn, licenses, element, license_operator_chars), d)
|
||||
|
||||
def skip_incompatible_package_licenses(d, pkgs):
|
||||
if not pkgs:
|
||||
return {}
|
||||
|
||||
pn = d.getVar("PN")
|
||||
|
||||
check_license = False if pn.startswith("nativesdk-") else True
|
||||
for t in ["-native", "-cross-${TARGET_ARCH}", "-cross-initial-${TARGET_ARCH}",
|
||||
"-crosssdk-${SDK_SYS}", "-crosssdk-initial-${SDK_SYS}",
|
||||
"-cross-canadian-${TRANSLATED_TARGET_ARCH}"]:
|
||||
if pn.endswith(d.expand(t)):
|
||||
check_license = False
|
||||
if pn.startswith("gcc-source-"):
|
||||
check_license = False
|
||||
|
||||
bad_licenses = (d.getVar('INCOMPATIBLE_LICENSE') or "").split()
|
||||
if not check_license or not bad_licenses:
|
||||
return {}
|
||||
|
||||
bad_licenses = expand_wildcard_licenses(d, bad_licenses)
|
||||
|
||||
exceptions = (d.getVar("INCOMPATIBLE_LICENSE_EXCEPTIONS") or "").split()
|
||||
|
||||
for lic_exception in exceptions:
|
||||
if ":" in lic_exception:
|
||||
lic_exception = lic_exception.split(":")[1]
|
||||
if lic_exception in obsolete_license_list():
|
||||
bb.fatal("Obsolete license %s used in INCOMPATIBLE_LICENSE_EXCEPTIONS" % lic_exception)
|
||||
|
||||
skipped_pkgs = {}
|
||||
for pkg in pkgs:
|
||||
remaining_bad_licenses = apply_pkg_license_exception(pkg, bad_licenses, exceptions)
|
||||
|
||||
incompatible_lic = incompatible_license(d, remaining_bad_licenses, pkg)
|
||||
if incompatible_lic:
|
||||
skipped_pkgs[pkg] = incompatible_lic
|
||||
|
||||
return skipped_pkgs
|
||||
|
||||
1096
meta/lib/oe/sbom30.py
Normal file
1096
meta/lib/oe/sbom30.py
Normal file
File diff suppressed because it is too large
Load Diff
5593
meta/lib/oe/spdx30.py
Normal file
5593
meta/lib/oe/spdx30.py
Normal file
File diff suppressed because it is too large
Load Diff
1343
meta/lib/oe/spdx30_tasks.py
Normal file
1343
meta/lib/oe/spdx30_tasks.py
Normal file
File diff suppressed because it is too large
Load Diff
244
meta/lib/oe/spdx_common.py
Normal file
244
meta/lib/oe/spdx_common.py
Normal file
@@ -0,0 +1,244 @@
|
||||
#
|
||||
# Copyright OpenEmbedded Contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
|
||||
import bb
|
||||
import collections
|
||||
import json
|
||||
import oe.packagedata
|
||||
import re
|
||||
import shutil
|
||||
|
||||
from pathlib import Path
|
||||
from dataclasses import dataclass
|
||||
|
||||
LIC_REGEX = re.compile(
|
||||
rb"^\W*SPDX-License-Identifier:\s*([ \w\d.()+-]+?)(?:\s+\W*)?$",
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
|
||||
def extract_licenses(filename):
|
||||
"""
|
||||
Extract SPDX License identifiers from a file
|
||||
"""
|
||||
try:
|
||||
with open(filename, "rb") as f:
|
||||
size = min(15000, os.stat(filename).st_size)
|
||||
txt = f.read(size)
|
||||
licenses = re.findall(LIC_REGEX, txt)
|
||||
if licenses:
|
||||
ascii_licenses = [lic.decode("ascii") for lic in licenses]
|
||||
return ascii_licenses
|
||||
except Exception as e:
|
||||
bb.warn(f"Exception reading {filename}: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def is_work_shared_spdx(d):
|
||||
return '/work-shared/' in d.getVar('S')
|
||||
|
||||
|
||||
def load_spdx_license_data(d):
|
||||
with open(d.getVar("SPDX_LICENSES"), "r") as f:
|
||||
data = json.load(f)
|
||||
# Transform the license array to a dictionary
|
||||
data["licenses"] = {l["licenseId"]: l for l in data["licenses"]}
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def process_sources(d):
|
||||
"""
|
||||
Returns True if the sources for this recipe should be included in the SPDX
|
||||
or False if not
|
||||
"""
|
||||
pn = d.getVar("PN")
|
||||
assume_provided = (d.getVar("ASSUME_PROVIDED") or "").split()
|
||||
if pn in assume_provided:
|
||||
for p in d.getVar("PROVIDES").split():
|
||||
if p != pn:
|
||||
pn = p
|
||||
break
|
||||
|
||||
# glibc-locale: do_fetch, do_unpack and do_patch tasks have been deleted,
|
||||
# so avoid archiving source here.
|
||||
if pn.startswith("glibc-locale"):
|
||||
return False
|
||||
if d.getVar("PN") == "libtool-cross":
|
||||
return False
|
||||
if d.getVar("PN") == "libgcc-initial":
|
||||
return False
|
||||
if d.getVar("PN") == "shadow-sysroot":
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Dep(object):
|
||||
pn: str
|
||||
hashfn: str
|
||||
in_taskhash: bool
|
||||
|
||||
|
||||
def collect_direct_deps(d, dep_task):
|
||||
"""
|
||||
Find direct dependencies of current task
|
||||
|
||||
Returns the list of recipes that have a dep_task that the current task
|
||||
depends on
|
||||
"""
|
||||
current_task = "do_" + d.getVar("BB_CURRENTTASK")
|
||||
pn = d.getVar("PN")
|
||||
|
||||
taskdepdata = d.getVar("BB_TASKDEPDATA", False)
|
||||
|
||||
for this_dep in taskdepdata.values():
|
||||
if this_dep[0] == pn and this_dep[1] == current_task:
|
||||
break
|
||||
else:
|
||||
bb.fatal(f"Unable to find this {pn}:{current_task} in taskdepdata")
|
||||
|
||||
deps = set()
|
||||
|
||||
for dep_name in this_dep.deps:
|
||||
dep_data = taskdepdata[dep_name]
|
||||
if dep_data.taskname == dep_task and dep_data.pn != pn:
|
||||
deps.add((dep_data.pn, dep_data.hashfn, dep_name in this_dep.taskhash_deps))
|
||||
|
||||
return sorted(deps)
|
||||
|
||||
|
||||
def get_spdx_deps(d):
|
||||
"""
|
||||
Reads the SPDX dependencies JSON file and returns the data
|
||||
"""
|
||||
spdx_deps_file = Path(d.getVar("SPDXDEPS"))
|
||||
|
||||
deps = []
|
||||
with spdx_deps_file.open("r") as f:
|
||||
for d in json.load(f):
|
||||
deps.append(Dep(*d))
|
||||
return deps
|
||||
|
||||
|
||||
def collect_package_providers(d):
|
||||
"""
|
||||
Returns a dictionary where each RPROVIDES is mapped to the package that
|
||||
provides it
|
||||
"""
|
||||
deploy_dir_spdx = Path(d.getVar("DEPLOY_DIR_SPDX"))
|
||||
|
||||
providers = {}
|
||||
|
||||
deps = collect_direct_deps(d, "do_create_spdx")
|
||||
deps.append((d.getVar("PN"), d.getVar("BB_HASHFILENAME"), True))
|
||||
|
||||
for dep_pn, dep_hashfn, _ in deps:
|
||||
localdata = d
|
||||
recipe_data = oe.packagedata.read_pkgdata(dep_pn, localdata)
|
||||
if not recipe_data:
|
||||
localdata = bb.data.createCopy(d)
|
||||
localdata.setVar("PKGDATA_DIR", "${PKGDATA_DIR_SDK}")
|
||||
recipe_data = oe.packagedata.read_pkgdata(dep_pn, localdata)
|
||||
|
||||
for pkg in recipe_data.get("PACKAGES", "").split():
|
||||
pkg_data = oe.packagedata.read_subpkgdata_dict(pkg, localdata)
|
||||
rprovides = set(
|
||||
n
|
||||
for n, _ in bb.utils.explode_dep_versions2(
|
||||
pkg_data.get("RPROVIDES", "")
|
||||
).items()
|
||||
)
|
||||
rprovides.add(pkg)
|
||||
|
||||
if "PKG" in pkg_data:
|
||||
pkg = pkg_data["PKG"]
|
||||
rprovides.add(pkg)
|
||||
|
||||
for r in rprovides:
|
||||
providers[r] = (pkg, dep_hashfn)
|
||||
|
||||
return providers
|
||||
|
||||
|
||||
def get_patched_src(d):
|
||||
"""
|
||||
Save patched source of the recipe in SPDX_WORKDIR.
|
||||
"""
|
||||
spdx_workdir = d.getVar("SPDXWORK")
|
||||
spdx_sysroot_native = d.getVar("STAGING_DIR_NATIVE")
|
||||
pn = d.getVar("PN")
|
||||
|
||||
workdir = d.getVar("WORKDIR")
|
||||
|
||||
try:
|
||||
# The kernel class functions require it to be on work-shared, so we dont change WORKDIR
|
||||
if not is_work_shared_spdx(d):
|
||||
# Change the WORKDIR to make do_unpack do_patch run in another dir.
|
||||
d.setVar("WORKDIR", spdx_workdir)
|
||||
# Restore the original path to recipe's native sysroot (it's relative to WORKDIR).
|
||||
d.setVar("STAGING_DIR_NATIVE", spdx_sysroot_native)
|
||||
|
||||
# The changed 'WORKDIR' also caused 'B' changed, create dir 'B' for the
|
||||
# possibly requiring of the following tasks (such as some recipes's
|
||||
# do_patch required 'B' existed).
|
||||
bb.utils.mkdirhier(d.getVar("B"))
|
||||
|
||||
bb.build.exec_func("do_unpack", d)
|
||||
|
||||
if d.getVar("SRC_URI") != "":
|
||||
if bb.data.inherits_class('dos2unix', d):
|
||||
bb.build.exec_func('do_convert_crlf_to_lf', d)
|
||||
bb.build.exec_func("do_patch", d)
|
||||
|
||||
# Copy source from work-share to spdx_workdir
|
||||
if is_work_shared_spdx(d):
|
||||
share_src = d.getVar('S')
|
||||
d.setVar("WORKDIR", spdx_workdir)
|
||||
d.setVar("STAGING_DIR_NATIVE", spdx_sysroot_native)
|
||||
# Copy source to ${SPDXWORK}, same basename dir of ${S};
|
||||
src_dir = (
|
||||
spdx_workdir
|
||||
+ "/"
|
||||
+ os.path.basename(share_src)
|
||||
)
|
||||
# For kernel souce, rename suffix dir 'kernel-source'
|
||||
# to ${BP} (${BPN}-${PV})
|
||||
if bb.data.inherits_class("kernel", d):
|
||||
src_dir = spdx_workdir + "/" + d.getVar('BP')
|
||||
|
||||
bb.note(f"copyhardlinktree {share_src} to {src_dir}")
|
||||
oe.path.copyhardlinktree(share_src, src_dir)
|
||||
|
||||
# Some userland has no source.
|
||||
if not os.path.exists(spdx_workdir):
|
||||
bb.utils.mkdirhier(spdx_workdir)
|
||||
finally:
|
||||
d.setVar("WORKDIR", workdir)
|
||||
|
||||
|
||||
def has_task(d, task):
|
||||
return bool(d.getVarFlag(task, "task", False)) and not bool(d.getVarFlag(task, "noexec", False))
|
||||
|
||||
|
||||
def fetch_data_to_uri(fd, name):
|
||||
"""
|
||||
Translates a bitbake FetchData to a string URI
|
||||
"""
|
||||
uri = fd.type
|
||||
# Map gitsm to git, since gitsm:// is not a valid URI protocol
|
||||
if uri == "gitsm":
|
||||
uri = "git"
|
||||
proto = getattr(fd, "proto", None)
|
||||
if proto is not None:
|
||||
uri = uri + "+" + proto
|
||||
uri = uri + "://" + fd.host + fd.path
|
||||
|
||||
if fd.method.supports_srcrev():
|
||||
uri = uri + "@" + fd.revision
|
||||
|
||||
return uri
|
||||
@@ -18,6 +18,13 @@ class PingTest(OERuntimeTestCase):
|
||||
output = ''
|
||||
count = 0
|
||||
self.assertNotEqual(len(self.target.ip), 0, msg="No target IP address set")
|
||||
|
||||
# If the target IP is localhost (because user-space networking is being used),
|
||||
# then there's no point in pinging it.
|
||||
if self.target.ip.startswith("127.0.0.") or self.target.ip in ("localhost", "::1"):
|
||||
print("runtime/ping: localhost detected, not pinging")
|
||||
return
|
||||
|
||||
try:
|
||||
while count < 5:
|
||||
cmd = 'ping -c 1 %s' % self.target.ip
|
||||
|
||||
@@ -476,8 +476,8 @@ class DevtoolAddTests(DevtoolBase):
|
||||
version = 'v3.1.0'
|
||||
pn = 'mbedtls'
|
||||
# this will trigger reformat_git_uri with branch parameter in url
|
||||
git_url = "'git://git@github.com/ARMmbed/mbedtls.git;branch=mbedtls-2.28;protocol=https'"
|
||||
resulting_src_uri = "git://git@github.com/ARMmbed/mbedtls.git;branch=mbedtls-2.28;protocol=https"
|
||||
git_url = "'git://git@github.com/Mbed-TLS/mbedtls.git;branch=archive/mbedtls-2.28;protocol=https'"
|
||||
resulting_src_uri = "git://git@github.com/Mbed-TLS/mbedtls.git;branch=archive/mbedtls-2.28;protocol=https"
|
||||
self._test_devtool_add_git_url(git_url, version, pn, resulting_src_uri)
|
||||
|
||||
def test_devtool_add_git_style2(self):
|
||||
@@ -485,8 +485,8 @@ class DevtoolAddTests(DevtoolBase):
|
||||
srcrev = 'v3.1.0'
|
||||
pn = 'mbedtls'
|
||||
# this will trigger reformat_git_uri with branch parameter in url
|
||||
git_url = "'git://git@github.com/ARMmbed/mbedtls.git;protocol=https'"
|
||||
resulting_src_uri = "git://git@github.com/ARMmbed/mbedtls.git;protocol=https;branch=master"
|
||||
git_url = "'git://git@github.com/Mbed-TLS/mbedtls.git;protocol=https'"
|
||||
resulting_src_uri = "git://git@github.com/Mbed-TLS/mbedtls.git;protocol=https;branch=master"
|
||||
self._test_devtool_add_git_url(git_url, version, pn, resulting_src_uri, srcrev)
|
||||
|
||||
def test_devtool_add_library(self):
|
||||
|
||||
@@ -6,29 +6,39 @@
|
||||
|
||||
import json
|
||||
import os
|
||||
import textwrap
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
from oeqa.selftest.case import OESelftestTestCase
|
||||
from oeqa.utils.commands import bitbake, get_bb_var, runCmd
|
||||
from oeqa.utils.commands import bitbake, get_bb_var, get_bb_vars, runCmd
|
||||
import oe.spdx30
|
||||
|
||||
class SPDXCheck(OESelftestTestCase):
|
||||
|
||||
class SPDX22Check(OESelftestTestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(SPDXCheck, cls).setUpClass()
|
||||
super().setUpClass()
|
||||
bitbake("python3-spdx-tools-native")
|
||||
bitbake("-c addto_recipe_sysroot python3-spdx-tools-native")
|
||||
|
||||
def check_recipe_spdx(self, high_level_dir, spdx_file, target_name):
|
||||
config = """
|
||||
INHERIT += "create-spdx"
|
||||
"""
|
||||
config = textwrap.dedent(
|
||||
"""\
|
||||
INHERIT:remove = "create-spdx"
|
||||
INHERIT += "create-spdx-2.2"
|
||||
"""
|
||||
)
|
||||
self.write_config(config)
|
||||
|
||||
deploy_dir = get_bb_var("DEPLOY_DIR")
|
||||
machine_var = get_bb_var("MACHINE")
|
||||
arch_dir = get_bb_var("PACKAGE_ARCH", target_name)
|
||||
spdx_version = get_bb_var("SPDX_VERSION")
|
||||
# qemux86-64 creates the directory qemux86_64
|
||||
machine_dir = machine_var.replace("-", "_")
|
||||
#arch_dir = arch_var.replace("-", "_")
|
||||
|
||||
full_file_path = os.path.join(deploy_dir, "spdx", machine_dir, high_level_dir, spdx_file)
|
||||
full_file_path = os.path.join(
|
||||
deploy_dir, "spdx", spdx_version, arch_dir, high_level_dir, spdx_file
|
||||
)
|
||||
|
||||
try:
|
||||
os.remove(full_file_path)
|
||||
@@ -43,8 +53,13 @@ INHERIT += "create-spdx"
|
||||
self.assertNotEqual(report, None)
|
||||
self.assertNotEqual(report["SPDXID"], None)
|
||||
|
||||
python = os.path.join(get_bb_var('STAGING_BINDIR', 'python3-spdx-tools-native'), 'nativepython3')
|
||||
validator = os.path.join(get_bb_var('STAGING_BINDIR', 'python3-spdx-tools-native'), 'pyspdxtools')
|
||||
python = os.path.join(
|
||||
get_bb_var("STAGING_BINDIR", "python3-spdx-tools-native"),
|
||||
"nativepython3",
|
||||
)
|
||||
validator = os.path.join(
|
||||
get_bb_var("STAGING_BINDIR", "python3-spdx-tools-native"), "pyspdxtools"
|
||||
)
|
||||
result = runCmd("{} {} -i {}".format(python, validator, filename))
|
||||
|
||||
self.assertExists(full_file_path)
|
||||
@@ -52,3 +67,222 @@ INHERIT += "create-spdx"
|
||||
|
||||
def test_spdx_base_files(self):
|
||||
self.check_recipe_spdx("packages", "base-files.spdx.json", "base-files")
|
||||
|
||||
def test_spdx_tar(self):
|
||||
self.check_recipe_spdx("packages", "tar.spdx.json", "tar")
|
||||
|
||||
|
||||
class SPDX3CheckBase(object):
|
||||
"""
|
||||
Base class for checking SPDX 3 based tests
|
||||
"""
|
||||
|
||||
def check_spdx_file(self, filename):
|
||||
self.assertExists(filename)
|
||||
|
||||
# Read the file
|
||||
objset = oe.spdx30.SHACLObjectSet()
|
||||
with open(filename, "r") as f:
|
||||
d = oe.spdx30.JSONLDDeserializer()
|
||||
d.read(f, objset)
|
||||
|
||||
return objset
|
||||
|
||||
def check_recipe_spdx(self, target_name, spdx_path, *, task=None, extraconf=""):
|
||||
config = (
|
||||
textwrap.dedent(
|
||||
f"""\
|
||||
INHERIT:remove = "create-spdx"
|
||||
INHERIT += "{self.SPDX_CLASS}"
|
||||
"""
|
||||
)
|
||||
+ textwrap.dedent(extraconf)
|
||||
)
|
||||
|
||||
self.write_config(config)
|
||||
|
||||
if task:
|
||||
bitbake(f"-c {task} {target_name}")
|
||||
else:
|
||||
bitbake(target_name)
|
||||
|
||||
filename = spdx_path.format(
|
||||
**get_bb_vars(
|
||||
[
|
||||
"DEPLOY_DIR_IMAGE",
|
||||
"DEPLOY_DIR_SPDX",
|
||||
"MACHINE",
|
||||
"MACHINE_ARCH",
|
||||
"SDKMACHINE",
|
||||
"SDK_DEPLOY",
|
||||
"SPDX_VERSION",
|
||||
"SSTATE_PKGARCH",
|
||||
"TOOLCHAIN_OUTPUTNAME",
|
||||
],
|
||||
target_name,
|
||||
)
|
||||
)
|
||||
|
||||
return self.check_spdx_file(filename)
|
||||
|
||||
def check_objset_missing_ids(self, objset):
|
||||
for o in objset.foreach_type(oe.spdx30.SpdxDocument):
|
||||
doc = o
|
||||
break
|
||||
else:
|
||||
self.assertTrue(False, "Unable to find SpdxDocument")
|
||||
|
||||
missing_ids = objset.missing_ids - set(i.externalSpdxId for i in doc.import_)
|
||||
if missing_ids:
|
||||
self.assertTrue(
|
||||
False,
|
||||
"The following SPDXIDs are unresolved:\n " + "\n ".join(missing_ids),
|
||||
)
|
||||
|
||||
|
||||
class SPDX30Check(SPDX3CheckBase, OESelftestTestCase):
|
||||
SPDX_CLASS = "create-spdx-3.0"
|
||||
|
||||
def test_base_files(self):
|
||||
self.check_recipe_spdx(
|
||||
"base-files",
|
||||
"{DEPLOY_DIR_SPDX}/{MACHINE_ARCH}/packages/package-base-files.spdx.json",
|
||||
)
|
||||
|
||||
def test_gcc_include_source(self):
|
||||
objset = self.check_recipe_spdx(
|
||||
"gcc",
|
||||
"{DEPLOY_DIR_SPDX}/{SSTATE_PKGARCH}/recipes/recipe-gcc.spdx.json",
|
||||
extraconf="""\
|
||||
SPDX_INCLUDE_SOURCES = "1"
|
||||
""",
|
||||
)
|
||||
|
||||
gcc_pv = get_bb_var("PV", "gcc")
|
||||
filename = f"gcc-{gcc_pv}/README"
|
||||
found = False
|
||||
for software_file in objset.foreach_type(oe.spdx30.software_File):
|
||||
if software_file.name == filename:
|
||||
found = True
|
||||
self.logger.info(
|
||||
f"The spdxId of {filename} in recipe-gcc.spdx.json is {software_file.spdxId}"
|
||||
)
|
||||
break
|
||||
|
||||
self.assertTrue(
|
||||
found, f"Not found source file {filename} in recipe-gcc.spdx.json\n"
|
||||
)
|
||||
|
||||
def test_core_image_minimal(self):
|
||||
objset = self.check_recipe_spdx(
|
||||
"core-image-minimal",
|
||||
"{DEPLOY_DIR_IMAGE}/core-image-minimal-{MACHINE}.rootfs.spdx.json",
|
||||
)
|
||||
|
||||
# Document should be fully linked
|
||||
self.check_objset_missing_ids(objset)
|
||||
|
||||
def test_core_image_minimal_sdk(self):
|
||||
objset = self.check_recipe_spdx(
|
||||
"core-image-minimal",
|
||||
"{SDK_DEPLOY}/{TOOLCHAIN_OUTPUTNAME}.spdx.json",
|
||||
task="populate_sdk",
|
||||
)
|
||||
|
||||
# Document should be fully linked
|
||||
self.check_objset_missing_ids(objset)
|
||||
|
||||
def test_baremetal_helloworld(self):
|
||||
objset = self.check_recipe_spdx(
|
||||
"baremetal-helloworld",
|
||||
"{DEPLOY_DIR_IMAGE}/baremetal-helloworld-image-{MACHINE}.spdx.json",
|
||||
extraconf="""\
|
||||
TCLIBC = "baremetal"
|
||||
""",
|
||||
)
|
||||
|
||||
# Document should be fully linked
|
||||
self.check_objset_missing_ids(objset)
|
||||
|
||||
def test_extra_opts(self):
|
||||
HOST_SPDXID = "http://foo.bar/spdx/bar2"
|
||||
|
||||
EXTRACONF = textwrap.dedent(
|
||||
f"""\
|
||||
SPDX_INVOKED_BY_name = "CI Tool"
|
||||
SPDX_INVOKED_BY_type = "software"
|
||||
|
||||
SPDX_ON_BEHALF_OF_name = "John Doe"
|
||||
SPDX_ON_BEHALF_OF_type = "person"
|
||||
SPDX_ON_BEHALF_OF_id_email = "John.Doe@noreply.com"
|
||||
|
||||
SPDX_PACKAGE_SUPPLIER_name = "ACME Embedded Widgets"
|
||||
SPDX_PACKAGE_SUPPLIER_type = "organization"
|
||||
|
||||
SPDX_AUTHORS += "authorA"
|
||||
SPDX_AUTHORS_authorA_ref = "SPDX_ON_BEHALF_OF"
|
||||
|
||||
SPDX_BUILD_HOST = "host"
|
||||
|
||||
SPDX_IMPORTS += "host"
|
||||
SPDX_IMPORTS_host_spdxid = "{HOST_SPDXID}"
|
||||
|
||||
SPDX_INCLUDE_BUILD_VARIABLES = "1"
|
||||
SPDX_INCLUDE_BITBAKE_PARENT_BUILD = "1"
|
||||
SPDX_INCLUDE_TIMESTAMPS = "1"
|
||||
|
||||
SPDX_PRETTY = "1"
|
||||
"""
|
||||
)
|
||||
extraconf_hash = hashlib.sha1(EXTRACONF.encode("utf-8")).hexdigest()
|
||||
|
||||
objset = self.check_recipe_spdx(
|
||||
"core-image-minimal",
|
||||
"{DEPLOY_DIR_IMAGE}/core-image-minimal-{MACHINE}.rootfs.spdx.json",
|
||||
# Many SPDX variables do not trigger a rebuild, since they are
|
||||
# intended to record information at the time of the build. As such,
|
||||
# the extra configuration alone may not trigger a rebuild, and even
|
||||
# if it does, the task hash won't necessarily be unique. In order
|
||||
# to make sure rebuilds happen, but still allow these test objects
|
||||
# to be pulled from sstate (e.g. remain reproducible), change the
|
||||
# namespace prefix to include the hash of the extra configuration
|
||||
extraconf=textwrap.dedent(
|
||||
f"""\
|
||||
SPDX_NAMESPACE_PREFIX = "http://spdx.org/spdxdocs/{extraconf_hash}"
|
||||
"""
|
||||
)
|
||||
+ EXTRACONF,
|
||||
)
|
||||
|
||||
# Document should be fully linked
|
||||
self.check_objset_missing_ids(objset)
|
||||
|
||||
for o in objset.foreach_type(oe.spdx30.SoftwareAgent):
|
||||
if o.name == "CI Tool":
|
||||
break
|
||||
else:
|
||||
self.assertTrue(False, "Unable to find software tool")
|
||||
|
||||
for o in objset.foreach_type(oe.spdx30.Person):
|
||||
if o.name == "John Doe":
|
||||
break
|
||||
else:
|
||||
self.assertTrue(False, "Unable to find person")
|
||||
|
||||
for o in objset.foreach_type(oe.spdx30.Organization):
|
||||
if o.name == "ACME Embedded Widgets":
|
||||
break
|
||||
else:
|
||||
self.assertTrue(False, "Unable to find organization")
|
||||
|
||||
for o in objset.foreach_type(oe.spdx30.SpdxDocument):
|
||||
doc = o
|
||||
break
|
||||
else:
|
||||
self.assertTrue(False, "Unable to find SpdxDocument")
|
||||
|
||||
for i in doc.import_:
|
||||
if i.externalSpdxId == HOST_SPDXID:
|
||||
break
|
||||
else:
|
||||
self.assertTrue(False, "Unable to find imported Host SpdxID")
|
||||
|
||||
56
meta/recipes-bsp/u-boot/files/CVE-2024-42040.patch
Normal file
56
meta/recipes-bsp/u-boot/files/CVE-2024-42040.patch
Normal file
@@ -0,0 +1,56 @@
|
||||
From 1406fc918977bba4dac0af5e22e63a5553aa6aff Mon Sep 17 00:00:00 2001
|
||||
From: Paul HENRYS <paul.henrys_ext@softathome.com>
|
||||
Date: Thu, 9 Oct 2025 17:43:28 +0200
|
||||
Subject: [PATCH] net: bootp: Prevent buffer overflow to avoid leaking the RAM
|
||||
content
|
||||
|
||||
CVE-2024-42040 describes a possible buffer overflow when calling
|
||||
bootp_process_vendor() in bootp_handler() since the total length
|
||||
of the packet is passed to bootp_process_vendor() without being
|
||||
reduced to len-(offsetof(struct bootp_hdr,bp_vend)+4).
|
||||
|
||||
The packet length is also checked against its minimum size to avoid
|
||||
reading data from struct bootp_hdr outside of the packet length.
|
||||
|
||||
Signed-off-by: Paul HENRYS <paul.henrys_ext@softathome.com>
|
||||
Signed-off-by: Philippe Reynes <philippe.reynes@softathome.com>
|
||||
|
||||
CVE: CVE-2024-42040
|
||||
Upstream-Status: Backport [https://source.denx.de/u-boot/u-boot/-/commit/81e5708cc2c865df606e49aed5415adb2a662171]
|
||||
Signed-off-by: Hongxu Jia <hongxu.jia@windriver.com>
|
||||
---
|
||||
net/bootp.c | 11 ++++++++++-
|
||||
1 file changed, 10 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/net/bootp.c b/net/bootp.c
|
||||
index 68002909634..843180d296c 100644
|
||||
--- a/net/bootp.c
|
||||
+++ b/net/bootp.c
|
||||
@@ -362,6 +362,14 @@ static void bootp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
|
||||
debug("got BOOTP packet (src=%d, dst=%d, len=%d want_len=%zu)\n",
|
||||
src, dest, len, sizeof(struct bootp_hdr));
|
||||
|
||||
+ /* Check the minimum size of a BOOTP packet is respected.
|
||||
+ * A BOOTP packet is between 300 bytes and 576 bytes big
|
||||
+ */
|
||||
+ if (len < offsetof(struct bootp_hdr, bp_vend) + 64) {
|
||||
+ printf("Error: got an invalid BOOTP packet (len=%u)\n", len);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
bp = (struct bootp_hdr *)pkt;
|
||||
|
||||
/* Filter out pkts we don't want */
|
||||
@@ -379,7 +387,8 @@ static void bootp_handler(uchar *pkt, unsigned dest, struct in_addr sip,
|
||||
|
||||
/* Retrieve extended information (we must parse the vendor area) */
|
||||
if (net_read_u32((u32 *)&bp->bp_vend[0]) == htonl(BOOTP_VENDOR_MAGIC))
|
||||
- bootp_process_vendor((uchar *)&bp->bp_vend[4], len);
|
||||
+ bootp_process_vendor((uchar *)&bp->bp_vend[4], len -
|
||||
+ (offsetof(struct bootp_hdr, bp_vend) + 4));
|
||||
|
||||
net_set_timeout_handler(0, (thand_f *)0);
|
||||
bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP, "bootp_stop");
|
||||
--
|
||||
2.49.0
|
||||
|
||||
@@ -23,6 +23,7 @@ SRC_URI = "git://source.denx.de/u-boot/u-boot.git;protocol=https;branch=master \
|
||||
file://CVE-2024-57258-2.patch \
|
||||
file://CVE-2024-57258-3.patch \
|
||||
file://CVE-2024-57259.patch \
|
||||
file://CVE-2024-42040.patch \
|
||||
"
|
||||
|
||||
S = "${WORKDIR}/git"
|
||||
|
||||
@@ -20,7 +20,7 @@ SRC_URI = "https://ftp.isc.org/isc/bind9/${PV}/${BPN}-${PV}.tar.xz \
|
||||
file://0001-avoid-start-failure-with-bind-user.patch \
|
||||
"
|
||||
|
||||
SRC_URI[sha256sum] = "fb373fac5ebbc41c645160afd5a9fb451918f6c0e69ab1d9474154e2b515de40"
|
||||
SRC_URI[sha256sum] = "6ddc1d981511c4da0b203b0513af131e5d15e5f1c261145736fe1f35dd1fe79d"
|
||||
|
||||
UPSTREAM_CHECK_URI = "https://ftp.isc.org/isc/bind9/"
|
||||
# follow the ESV versions divisible by 2
|
||||
125
meta/recipes-connectivity/openssh/openssh/CVE-2025-61984.patch
Normal file
125
meta/recipes-connectivity/openssh/openssh/CVE-2025-61984.patch
Normal file
@@ -0,0 +1,125 @@
|
||||
From d45e13c956b296bf933901c4da2b61eb2ccd7582 Mon Sep 17 00:00:00 2001
|
||||
From: "djm@openbsd.org" <djm@openbsd.org>
|
||||
Date: Thu, 4 Sep 2025 00:29:09 +0000
|
||||
Subject: [PATCH] upstream: Improve rules for %-expansion of username.
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Usernames passed on the commandline will no longer be subject to
|
||||
% expansion. Some tools invoke ssh with connection information
|
||||
(i.e. usernames and host names) supplied from untrusted sources.
|
||||
These may contain % expansion sequences which could yield
|
||||
unexpected results.
|
||||
|
||||
Since openssh-9.6, all usernames have been subject to validity
|
||||
checking. This change tightens the validity checks by refusing
|
||||
usernames that include control characters (again, these can cause
|
||||
surprises when supplied adversarially).
|
||||
|
||||
This change also relaxes the validity checks in one small way:
|
||||
usernames supplied via the configuration file as literals (i.e.
|
||||
include no % expansion characters) are not subject to these
|
||||
validity checks. This allows usernames that contain arbitrary
|
||||
characters to be used, but only via configuration files. This
|
||||
is done on the basis that ssh's configuration is trusted.
|
||||
|
||||
Pointed out by David Leadbeater, ok deraadt@
|
||||
|
||||
OpenBSD-Commit-ID: e2f0c871fbe664aba30607321575e7c7fc798362
|
||||
|
||||
Slightly modified since variable expansion of user names was
|
||||
first released in 10.0, commit bd30cf784d6e8"
|
||||
|
||||
Upstream-Status: Backport [Upstream commit https://github.com/openssh/openssh-portable/commit/35d5917652106aede47621bb3f64044604164043]
|
||||
CVE: CVE-2025-61984
|
||||
Signed-off-by: David Nyström <david.nystrom@est.tech>
|
||||
---
|
||||
ssh.c | 26 +++++++++++++++++++++++---
|
||||
1 file changed, 23 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/ssh.c b/ssh.c
|
||||
index 48d93ddf2..9c49f98a8 100644
|
||||
--- a/ssh.c
|
||||
+++ b/ssh.c
|
||||
@@ -649,6 +649,8 @@ valid_ruser(const char *s)
|
||||
if (*s == '-')
|
||||
return 0;
|
||||
for (i = 0; s[i] != 0; i++) {
|
||||
+ if (iscntrl((u_char)s[i]))
|
||||
+ return 0;
|
||||
if (strchr("'`\";&<>|(){}", s[i]) != NULL)
|
||||
return 0;
|
||||
/* Disallow '-' after whitespace */
|
||||
@@ -671,6 +673,7 @@ main(int ac, char **av)
|
||||
int i, r, opt, exit_status, use_syslog, direct, timeout_ms;
|
||||
int was_addr, config_test = 0, opt_terminated = 0, want_final_pass = 0;
|
||||
char *p, *cp, *line, *argv0, *logfile;
|
||||
+ int user_on_commandline = 0, user_was_default = 0;
|
||||
char cname[NI_MAXHOST], thishost[NI_MAXHOST];
|
||||
struct stat st;
|
||||
struct passwd *pw;
|
||||
@@ -1016,8 +1019,10 @@ main(int ac, char **av)
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
- if (options.user == NULL)
|
||||
+ if (options.user == NULL) {
|
||||
options.user = optarg;
|
||||
+ user_on_commandline = 1;
|
||||
+ }
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
@@ -1120,6 +1125,7 @@ main(int ac, char **av)
|
||||
if (options.user == NULL) {
|
||||
options.user = tuser;
|
||||
tuser = NULL;
|
||||
+ user_on_commandline = 1;
|
||||
}
|
||||
free(tuser);
|
||||
if (options.port == -1 && tport != -1)
|
||||
@@ -1134,6 +1140,7 @@ main(int ac, char **av)
|
||||
if (options.user == NULL) {
|
||||
options.user = p;
|
||||
p = NULL;
|
||||
+ user_on_commandline = 1;
|
||||
}
|
||||
*cp++ = '\0';
|
||||
host = xstrdup(cp);
|
||||
@@ -1288,8 +1295,10 @@ main(int ac, char **av)
|
||||
if (fill_default_options(&options) != 0)
|
||||
cleanup_exit(255);
|
||||
|
||||
- if (options.user == NULL)
|
||||
+ if (options.user == NULL) {
|
||||
+ user_was_default = 1;
|
||||
options.user = xstrdup(pw->pw_name);
|
||||
+ }
|
||||
|
||||
/*
|
||||
* If ProxyJump option specified, then construct a ProxyCommand now.
|
||||
@@ -1430,11 +1439,22 @@ main(int ac, char **av)
|
||||
options.host_key_alias : options.host_arg);
|
||||
cinfo->host_arg = xstrdup(options.host_arg);
|
||||
cinfo->remhost = xstrdup(host);
|
||||
- cinfo->remuser = xstrdup(options.user);
|
||||
cinfo->homedir = xstrdup(pw->pw_dir);
|
||||
cinfo->locuser = xstrdup(pw->pw_name);
|
||||
cinfo->jmphost = xstrdup(options.jump_host == NULL ?
|
||||
"" : options.jump_host);
|
||||
+
|
||||
+ /*
|
||||
+ * Usernames specified on the commandline must be validated.
|
||||
+ * Conversely, usernames from getpwnam(3) or specified as literals
|
||||
+ * via configuration (i.e. not expanded) are not subject to validation.
|
||||
+ */
|
||||
+ if (user_on_commandline && !valid_ruser(options.user))
|
||||
+ fatal("remote username contains invalid characters");
|
||||
+
|
||||
+ /* Store it and calculate hash. */
|
||||
+ cinfo->remuser = xstrdup(options.user);
|
||||
+
|
||||
cinfo->conn_hash_hex = ssh_connection_hash(cinfo->thishost,
|
||||
cinfo->remhost, cinfo->portstr, cinfo->remuser, cinfo->jmphost);
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
From 54928cb9eaa7143ff17f463efa7ed3109afdbf30 Mon Sep 17 00:00:00 2001
|
||||
From: "djm@openbsd.org" <djm@openbsd.org>
|
||||
Date: Thu, 4 Sep 2025 00:30:06 +0000
|
||||
Subject: [PATCH] upstream: don't allow \0 characters in url-encoded strings.
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Suggested by David Leadbeater, ok deraadt@
|
||||
|
||||
OpenBSD-Commit-ID: c92196cef0f970ceabc1e8007a80b01e9b7cd49c
|
||||
|
||||
Upstream-Status: Backport [Upstream commit https://github.com/openssh/openssh-portable/commit/43b3bff47bb029f2299bacb6a36057981b39fdb0]
|
||||
CVE: CVE-2025-61985
|
||||
Signed-off-by: David Nyström <david.nystrom@est.tech>
|
||||
---
|
||||
misc.c | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/misc.c b/misc.c
|
||||
index 3db2e4d0b..cac246b63 100644
|
||||
--- a/misc.c
|
||||
+++ b/misc.c
|
||||
@@ -955,7 +955,7 @@ urldecode(const char *src)
|
||||
size_t srclen;
|
||||
|
||||
if ((srclen = strlen(src)) >= SIZE_MAX)
|
||||
- fatal_f("input too large");
|
||||
+ return NULL;
|
||||
ret = xmalloc(srclen + 1);
|
||||
for (dst = ret; *src != '\0'; src++) {
|
||||
switch (*src) {
|
||||
@@ -963,9 +963,10 @@ urldecode(const char *src)
|
||||
*dst++ = ' ';
|
||||
break;
|
||||
case '%':
|
||||
+ /* note: don't allow \0 characters */
|
||||
if (!isxdigit((unsigned char)src[1]) ||
|
||||
!isxdigit((unsigned char)src[2]) ||
|
||||
- (ch = hexchar(src + 1)) == -1) {
|
||||
+ (ch = hexchar(src + 1)) == -1 || ch == 0) {
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
--
|
||||
2.44.1
|
||||
|
||||
@@ -32,6 +32,8 @@ SRC_URI = "http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-${PV}.tar
|
||||
file://CVE-2025-26466.patch \
|
||||
file://CVE-2025-26465.patch \
|
||||
file://CVE-2025-32728.patch \
|
||||
file://CVE-2025-61985.patch \
|
||||
file://CVE-2025-61984.patch \
|
||||
"
|
||||
SRC_URI[sha256sum] = "910211c07255a8c5ad654391b40ee59800710dd8119dd5362de09385aa7a777c"
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
From 726432d7622cc0088ac353d073b59628b590ea44 Mon Sep 17 00:00:00 2001
|
||||
From: Jouni Malinen <j@w1.fi>
|
||||
Date: Sat, 25 Jan 2025 11:21:16 +0200
|
||||
Subject: [PATCH] RADIUS: Drop pending request only when accepting the response
|
||||
|
||||
The case of an invalid authenticator in a RADIUS response could imply
|
||||
that the response is not from the correct RADIUS server and as such,
|
||||
such a response should be discarded without changing internal state for
|
||||
the pending request. The case of an unknown response (RADIUS_RX_UNKNOWN)
|
||||
is somewhat more complex since it could have been indicated before
|
||||
validating the authenticator. In any case, it seems better to change the
|
||||
state for the pending request only when we have fully accepted the
|
||||
response.
|
||||
|
||||
Allowing the internal state of pending RADIUS request to change based on
|
||||
responses that are not fully validation could have allow at least a
|
||||
theoretical DoS attack if an attacker were to have means for injecting
|
||||
RADIUS messages to the network using the IP address of the real RADIUS
|
||||
server and being able to do so more quickly than the real server and
|
||||
with the matching identifier from the request header (i.e., either by
|
||||
flooding 256 responses quickly or by having means to capture the RADIUS
|
||||
request). These should not really be realistic options in a properly
|
||||
protected deployment, but nevertheless it is good to be more careful in
|
||||
processing RADIUS responses.
|
||||
|
||||
Remove a pending RADIUS request from the internal list only when having
|
||||
fully accepted a matching RADIUS response, i.e., after one of the
|
||||
registered handlers has confirmed that the authenticator is valid and
|
||||
processing of the response has succeeded.
|
||||
|
||||
Signed-off-by: Jouni Malinen <j@w1.fi>
|
||||
|
||||
CVE: CVE-2025-24912
|
||||
Upstream-Status: Backport [https://w1.fi/cgit/hostap/commit/?id=726432d7622cc0088ac353d073b59628b590ea44]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
src/radius/radius_client.c | 15 +++++++--------
|
||||
1 file changed, 7 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
|
||||
index 2a7f36170..7909b29a7 100644
|
||||
--- a/src/radius/radius_client.c
|
||||
+++ b/src/radius/radius_client.c
|
||||
@@ -922,13 +922,6 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||
roundtrip / 100, roundtrip % 100);
|
||||
rconf->round_trip_time = roundtrip;
|
||||
|
||||
- /* Remove ACKed RADIUS packet from retransmit list */
|
||||
- if (prev_req)
|
||||
- prev_req->next = req->next;
|
||||
- else
|
||||
- radius->msgs = req->next;
|
||||
- radius->num_msgs--;
|
||||
-
|
||||
for (i = 0; i < num_handlers; i++) {
|
||||
RadiusRxResult res;
|
||||
res = handlers[i].handler(msg, req->msg, req->shared_secret,
|
||||
@@ -939,6 +932,13 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||
radius_msg_free(msg);
|
||||
/* fall through */
|
||||
case RADIUS_RX_QUEUED:
|
||||
+ /* Remove ACKed RADIUS packet from retransmit list */
|
||||
+ if (prev_req)
|
||||
+ prev_req->next = req->next;
|
||||
+ else
|
||||
+ radius->msgs = req->next;
|
||||
+ radius->num_msgs--;
|
||||
+
|
||||
radius_client_msg_free(req);
|
||||
return;
|
||||
case RADIUS_RX_INVALID_AUTHENTICATOR:
|
||||
@@ -960,7 +960,6 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||
msg_type, hdr->code, hdr->identifier,
|
||||
invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
|
||||
"");
|
||||
- radius_client_msg_free(req);
|
||||
|
||||
fail:
|
||||
radius_msg_free(msg);
|
||||
@@ -0,0 +1,70 @@
|
||||
From 339a334551ca911187cc870f4f97ef08e11db109 Mon Sep 17 00:00:00 2001
|
||||
From: Jouni Malinen <quic_jouni@quicinc.com>
|
||||
Date: Wed, 5 Feb 2025 19:23:39 +0200
|
||||
Subject: [PATCH] RADIUS: Fix pending request dropping
|
||||
|
||||
A recent change to this moved the place where the processed RADIUS
|
||||
request was removed from the pending list to happen after the message
|
||||
handler had been called. This did not take into account possibility of
|
||||
the handler adding a new pending request in the list and the prev_req
|
||||
pointer not necessarily pointing to the correct entry anymore. As such,
|
||||
some of the pending requests could have been lost and that would result
|
||||
in not being able to process responses to those requests and also, to a
|
||||
memory leak.
|
||||
|
||||
Fix this by determining prev_req at the point when the pending request
|
||||
is being removed, i.e., after the handler function has already added a
|
||||
new entry.
|
||||
|
||||
Fixes: 726432d7622c ("RADIUS: Drop pending request only when accepting the response")
|
||||
Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>
|
||||
|
||||
CVE: CVE-2025-24912
|
||||
Upstream-Status: Backport [https://w1.fi/cgit/hostap/commit/?id=339a334551ca911187cc870f4f97ef08e11db109]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
src/radius/radius_client.c | 10 +++++++---
|
||||
1 file changed, 7 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
|
||||
index 7909b29a7..d4faa7936 100644
|
||||
--- a/src/radius/radius_client.c
|
||||
+++ b/src/radius/radius_client.c
|
||||
@@ -824,7 +824,7 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||
struct radius_hdr *hdr;
|
||||
struct radius_rx_handler *handlers;
|
||||
size_t num_handlers, i;
|
||||
- struct radius_msg_list *req, *prev_req;
|
||||
+ struct radius_msg_list *req, *prev_req, *r;
|
||||
struct os_reltime now;
|
||||
struct hostapd_radius_server *rconf;
|
||||
int invalid_authenticator = 0;
|
||||
@@ -887,7 +887,6 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||
break;
|
||||
}
|
||||
|
||||
- prev_req = NULL;
|
||||
req = radius->msgs;
|
||||
while (req) {
|
||||
/* TODO: also match by src addr:port of the packet when using
|
||||
@@ -899,7 +898,6 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||
hdr->identifier)
|
||||
break;
|
||||
|
||||
- prev_req = req;
|
||||
req = req->next;
|
||||
}
|
||||
|
||||
@@ -933,6 +931,12 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
|
||||
/* fall through */
|
||||
case RADIUS_RX_QUEUED:
|
||||
/* Remove ACKed RADIUS packet from retransmit list */
|
||||
+ prev_req = NULL;
|
||||
+ for (r = radius->msgs; r; r = r->next) {
|
||||
+ if (r == req)
|
||||
+ break;
|
||||
+ prev_req = r;
|
||||
+ }
|
||||
if (prev_req)
|
||||
prev_req->next = req->next;
|
||||
else
|
||||
@@ -36,6 +36,8 @@ SRC_URI = "http://w1.fi/releases/wpa_supplicant-${PV}.tar.gz \
|
||||
file://CVE-2022-37660-0003.patch \
|
||||
file://CVE-2022-37660-0004.patch \
|
||||
file://CVE-2022-37660-0005.patch \
|
||||
file://CVE-2025-24912-01.patch \
|
||||
file://CVE-2025-24912-02.patch \
|
||||
"
|
||||
SRC_URI[sha256sum] = "20df7ae5154b3830355f8ab4269123a87affdea59fe74fe9292a91d0d7e17b2f"
|
||||
|
||||
|
||||
52
meta/recipes-core/expat/expat/CVE-2025-59375-00.patch
Normal file
52
meta/recipes-core/expat/expat/CVE-2025-59375-00.patch
Normal file
@@ -0,0 +1,52 @@
|
||||
From 87321ac84a0d6cb42ee64a591adc79c1ec37fb5b Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Tue, 2 Sep 2025 20:52:29 +0200
|
||||
Subject: [PATCH] xmlwf: Mention supported environment variables in --help
|
||||
output
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/87321ac84a0d6cb42ee64a591adc79c1ec37fb5b]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
xmlwf/xmlwf.c | 8 ++++++++
|
||||
xmlwf/xmlwf_helpgen.py | 8 ++++++++
|
||||
2 files changed, 16 insertions(+)
|
||||
|
||||
diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
|
||||
index ec7e51c9..8cfc73ca 100644
|
||||
--- a/xmlwf/xmlwf.c
|
||||
+++ b/xmlwf/xmlwf.c
|
||||
@@ -926,6 +926,14 @@ usage(const XML_Char *prog, int rc) {
|
||||
T(" -h, --help show this [h]elp message and exit\n")
|
||||
T(" -v, --version show program's [v]ersion number and exit\n")
|
||||
T("\n")
|
||||
+ T("environment variables:\n")
|
||||
+ T(" EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)\n")
|
||||
+ T(" Control verbosity of accounting debugging (default: 0)\n")
|
||||
+ T(" EXPAT_ENTITY_DEBUG=(0|1)\n")
|
||||
+ T(" Control verbosity of entity debugging (default: 0)\n")
|
||||
+ T(" EXPAT_ENTROPY_DEBUG=(0|1)\n")
|
||||
+ T(" Control verbosity of entropy debugging (default: 0)\n")
|
||||
+ T("\n")
|
||||
T("exit status:\n")
|
||||
T(" 0 the input files are well-formed and the output (if requested) was written successfully\n")
|
||||
T(" 1 could not allocate data structures, signals a serious problem with execution environment\n")
|
||||
diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py
|
||||
index c3257f0e..39a3dc13 100755
|
||||
--- a/xmlwf/xmlwf_helpgen.py
|
||||
+++ b/xmlwf/xmlwf_helpgen.py
|
||||
@@ -32,6 +32,14 @@
|
||||
import argparse
|
||||
|
||||
epilog = """
|
||||
+environment variables:
|
||||
+ EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)
|
||||
+ Control verbosity of accounting debugging (default: 0)
|
||||
+ EXPAT_ENTITY_DEBUG=(0|1)
|
||||
+ Control verbosity of entity debugging (default: 0)
|
||||
+ EXPAT_ENTROPY_DEBUG=(0|1)
|
||||
+ Control verbosity of entropy debugging (default: 0)
|
||||
+
|
||||
exit status:
|
||||
0 the input files are well-formed and the output (if requested) was written successfully
|
||||
1 could not allocate data structures, signals a serious problem with execution environment
|
||||
48
meta/recipes-core/expat/expat/CVE-2025-59375-01.patch
Normal file
48
meta/recipes-core/expat/expat/CVE-2025-59375-01.patch
Normal file
@@ -0,0 +1,48 @@
|
||||
From 0872c189db6e457084fca335662a9cb49e8ec4c7 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 1 Sep 2025 18:06:59 +0200
|
||||
Subject: [PATCH] lib: Make function dtdCreate use macro MALLOC
|
||||
|
||||
.. and give its body access to the parser for upcoming changes
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/0872c189db6e457084fca335662a9cb49e8ec4c7]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 9 +++++----
|
||||
1 file changed, 5 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 25f786ec..b9d6eed1 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -555,7 +555,7 @@ static XML_Bool setContext(XML_Parser parser, const XML_Char *context);
|
||||
|
||||
static void FASTCALL normalizePublicId(XML_Char *s);
|
||||
|
||||
-static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms);
|
||||
+static DTD *dtdCreate(XML_Parser parser);
|
||||
/* do not call if m_parentParser != NULL */
|
||||
static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms);
|
||||
static void dtdDestroy(DTD *p, XML_Bool isDocEntity,
|
||||
@@ -1166,7 +1166,7 @@ parserCreate(const XML_Char *encodingName,
|
||||
if (dtd)
|
||||
parser->m_dtd = dtd;
|
||||
else {
|
||||
- parser->m_dtd = dtdCreate(&parser->m_mem);
|
||||
+ parser->m_dtd = dtdCreate(parser);
|
||||
if (parser->m_dtd == NULL) {
|
||||
FREE(parser, parser->m_dataBuf);
|
||||
FREE(parser, parser->m_atts);
|
||||
@@ -7126,8 +7126,9 @@ normalizePublicId(XML_Char *publicId) {
|
||||
}
|
||||
|
||||
static DTD *
|
||||
-dtdCreate(const XML_Memory_Handling_Suite *ms) {
|
||||
- DTD *p = ms->malloc_fcn(sizeof(DTD));
|
||||
+dtdCreate(XML_Parser parser) {
|
||||
+ const XML_Memory_Handling_Suite *const ms = &parser->m_mem;
|
||||
+ DTD *p = MALLOC(parser, sizeof(DTD));
|
||||
if (p == NULL)
|
||||
return p;
|
||||
poolInit(&(p->pool), ms);
|
||||
109
meta/recipes-core/expat/expat/CVE-2025-59375-02.patch
Normal file
109
meta/recipes-core/expat/expat/CVE-2025-59375-02.patch
Normal file
@@ -0,0 +1,109 @@
|
||||
From 8768dadae479d9f2e984b747fb2ba79bb78de94f Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 1 Sep 2025 18:10:26 +0200
|
||||
Subject: [PATCH] lib: Make string pools use macros MALLOC, FREE, REALLOC
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/8768dadae479d9f2e984b747fb2ba79bb78de94f]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 27 +++++++++++++--------------
|
||||
1 file changed, 13 insertions(+), 14 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index b9d6eed1..a56c71ea 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -357,7 +357,7 @@ typedef struct {
|
||||
const XML_Char *end;
|
||||
XML_Char *ptr;
|
||||
XML_Char *start;
|
||||
- const XML_Memory_Handling_Suite *mem;
|
||||
+ XML_Parser parser;
|
||||
} STRING_POOL;
|
||||
|
||||
/* The XML_Char before the name is used to determine whether
|
||||
@@ -574,8 +574,7 @@ static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter,
|
||||
const HASH_TABLE *table);
|
||||
static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *iter);
|
||||
|
||||
-static void FASTCALL poolInit(STRING_POOL *pool,
|
||||
- const XML_Memory_Handling_Suite *ms);
|
||||
+static void FASTCALL poolInit(STRING_POOL *pool, XML_Parser parser);
|
||||
static void FASTCALL poolClear(STRING_POOL *pool);
|
||||
static void FASTCALL poolDestroy(STRING_POOL *pool);
|
||||
static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
|
||||
@@ -1200,8 +1199,8 @@ parserCreate(const XML_Char *encodingName,
|
||||
|
||||
parser->m_protocolEncodingName = NULL;
|
||||
|
||||
- poolInit(&parser->m_tempPool, &(parser->m_mem));
|
||||
- poolInit(&parser->m_temp2Pool, &(parser->m_mem));
|
||||
+ poolInit(&parser->m_tempPool, parser);
|
||||
+ poolInit(&parser->m_temp2Pool, parser);
|
||||
parserInit(parser, encodingName);
|
||||
|
||||
if (encodingName && ! parser->m_protocolEncodingName) {
|
||||
@@ -7131,8 +7130,8 @@ dtdCreate(XML_Parser parser) {
|
||||
DTD *p = MALLOC(parser, sizeof(DTD));
|
||||
if (p == NULL)
|
||||
return p;
|
||||
- poolInit(&(p->pool), ms);
|
||||
- poolInit(&(p->entityValuePool), ms);
|
||||
+ poolInit(&(p->pool), parser);
|
||||
+ poolInit(&(p->entityValuePool), parser);
|
||||
hashTableInit(&(p->generalEntities), ms);
|
||||
hashTableInit(&(p->elementTypes), ms);
|
||||
hashTableInit(&(p->attributeIds), ms);
|
||||
@@ -7596,13 +7595,13 @@ hashTableIterNext(HASH_TABLE_ITER *iter) {
|
||||
}
|
||||
|
||||
static void FASTCALL
|
||||
-poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) {
|
||||
+poolInit(STRING_POOL *pool, XML_Parser parser) {
|
||||
pool->blocks = NULL;
|
||||
pool->freeBlocks = NULL;
|
||||
pool->start = NULL;
|
||||
pool->ptr = NULL;
|
||||
pool->end = NULL;
|
||||
- pool->mem = ms;
|
||||
+ pool->parser = parser;
|
||||
}
|
||||
|
||||
static void FASTCALL
|
||||
@@ -7629,13 +7628,13 @@ poolDestroy(STRING_POOL *pool) {
|
||||
BLOCK *p = pool->blocks;
|
||||
while (p) {
|
||||
BLOCK *tem = p->next;
|
||||
- pool->mem->free_fcn(p);
|
||||
+ FREE(pool->parser, p);
|
||||
p = tem;
|
||||
}
|
||||
p = pool->freeBlocks;
|
||||
while (p) {
|
||||
BLOCK *tem = p->next;
|
||||
- pool->mem->free_fcn(p);
|
||||
+ FREE(pool->parser, p);
|
||||
p = tem;
|
||||
}
|
||||
}
|
||||
@@ -7790,8 +7789,8 @@ poolGrow(STRING_POOL *pool) {
|
||||
if (bytesToAllocate == 0)
|
||||
return XML_FALSE;
|
||||
|
||||
- temp = (BLOCK *)pool->mem->realloc_fcn(pool->blocks,
|
||||
- (unsigned)bytesToAllocate);
|
||||
+ temp = (BLOCK *)REALLOC(pool->parser, pool->blocks,
|
||||
+ (unsigned)bytesToAllocate);
|
||||
if (temp == NULL)
|
||||
return XML_FALSE;
|
||||
pool->blocks = temp;
|
||||
@@ -7831,7 +7830,7 @@ poolGrow(STRING_POOL *pool) {
|
||||
if (bytesToAllocate == 0)
|
||||
return XML_FALSE;
|
||||
|
||||
- tem = pool->mem->malloc_fcn(bytesToAllocate);
|
||||
+ tem = MALLOC(pool->parser, bytesToAllocate);
|
||||
if (! tem)
|
||||
return XML_FALSE;
|
||||
tem->size = blockSize;
|
||||
127
meta/recipes-core/expat/expat/CVE-2025-59375-03.patch
Normal file
127
meta/recipes-core/expat/expat/CVE-2025-59375-03.patch
Normal file
@@ -0,0 +1,127 @@
|
||||
From 4fc6f1ee9f2b282cfe446bf645c992e37f8c3e15 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 1 Sep 2025 18:14:09 +0200
|
||||
Subject: [PATCH] lib: Make function hash tables use macros MALLOC and FREE
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4fc6f1ee9f2b282cfe446bf645c992e37f8c3e15]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 34 ++++++++++++++++------------------
|
||||
1 file changed, 16 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index a56c71ea..a65b0265 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -234,7 +234,7 @@ typedef struct {
|
||||
unsigned char power;
|
||||
size_t size;
|
||||
size_t used;
|
||||
- const XML_Memory_Handling_Suite *mem;
|
||||
+ XML_Parser parser;
|
||||
} HASH_TABLE;
|
||||
|
||||
static size_t keylen(KEY s);
|
||||
@@ -566,8 +566,7 @@ static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable,
|
||||
STRING_POOL *newPool, const HASH_TABLE *oldTable);
|
||||
static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name,
|
||||
size_t createSize);
|
||||
-static void FASTCALL hashTableInit(HASH_TABLE *table,
|
||||
- const XML_Memory_Handling_Suite *ms);
|
||||
+static void FASTCALL hashTableInit(HASH_TABLE *table, XML_Parser parser);
|
||||
static void FASTCALL hashTableClear(HASH_TABLE *table);
|
||||
static void FASTCALL hashTableDestroy(HASH_TABLE *table);
|
||||
static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter,
|
||||
@@ -7126,19 +7125,18 @@ normalizePublicId(XML_Char *publicId) {
|
||||
|
||||
static DTD *
|
||||
dtdCreate(XML_Parser parser) {
|
||||
- const XML_Memory_Handling_Suite *const ms = &parser->m_mem;
|
||||
DTD *p = MALLOC(parser, sizeof(DTD));
|
||||
if (p == NULL)
|
||||
return p;
|
||||
poolInit(&(p->pool), parser);
|
||||
poolInit(&(p->entityValuePool), parser);
|
||||
- hashTableInit(&(p->generalEntities), ms);
|
||||
- hashTableInit(&(p->elementTypes), ms);
|
||||
- hashTableInit(&(p->attributeIds), ms);
|
||||
- hashTableInit(&(p->prefixes), ms);
|
||||
+ hashTableInit(&(p->generalEntities), parser);
|
||||
+ hashTableInit(&(p->elementTypes), parser);
|
||||
+ hashTableInit(&(p->attributeIds), parser);
|
||||
+ hashTableInit(&(p->prefixes), parser);
|
||||
#ifdef XML_DTD
|
||||
p->paramEntityRead = XML_FALSE;
|
||||
- hashTableInit(&(p->paramEntities), ms);
|
||||
+ hashTableInit(&(p->paramEntities), parser);
|
||||
#endif /* XML_DTD */
|
||||
p->defaultPrefix.name = NULL;
|
||||
p->defaultPrefix.binding = NULL;
|
||||
@@ -7473,7 +7471,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
|
||||
/* table->size is a power of 2 */
|
||||
table->size = (size_t)1 << INIT_POWER;
|
||||
tsize = table->size * sizeof(NAMED *);
|
||||
- table->v = table->mem->malloc_fcn(tsize);
|
||||
+ table->v = MALLOC(table->parser, tsize);
|
||||
if (! table->v) {
|
||||
table->size = 0;
|
||||
return NULL;
|
||||
@@ -7513,7 +7511,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
|
||||
}
|
||||
|
||||
size_t tsize = newSize * sizeof(NAMED *);
|
||||
- NAMED **newV = table->mem->malloc_fcn(tsize);
|
||||
+ NAMED **newV = MALLOC(table->parser, tsize);
|
||||
if (! newV)
|
||||
return NULL;
|
||||
memset(newV, 0, tsize);
|
||||
@@ -7529,7 +7527,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
|
||||
}
|
||||
newV[j] = table->v[i];
|
||||
}
|
||||
- table->mem->free_fcn(table->v);
|
||||
+ FREE(table->parser, table->v);
|
||||
table->v = newV;
|
||||
table->power = newPower;
|
||||
table->size = newSize;
|
||||
@@ -7542,7 +7540,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
|
||||
}
|
||||
}
|
||||
}
|
||||
- table->v[i] = table->mem->malloc_fcn(createSize);
|
||||
+ table->v[i] = MALLOC(table->parser, createSize);
|
||||
if (! table->v[i])
|
||||
return NULL;
|
||||
memset(table->v[i], 0, createSize);
|
||||
@@ -7555,7 +7553,7 @@ static void FASTCALL
|
||||
hashTableClear(HASH_TABLE *table) {
|
||||
size_t i;
|
||||
for (i = 0; i < table->size; i++) {
|
||||
- table->mem->free_fcn(table->v[i]);
|
||||
+ FREE(table->parser, table->v[i]);
|
||||
table->v[i] = NULL;
|
||||
}
|
||||
table->used = 0;
|
||||
@@ -7565,17 +7563,17 @@ static void FASTCALL
|
||||
hashTableDestroy(HASH_TABLE *table) {
|
||||
size_t i;
|
||||
for (i = 0; i < table->size; i++)
|
||||
- table->mem->free_fcn(table->v[i]);
|
||||
- table->mem->free_fcn(table->v);
|
||||
+ FREE(table->parser, table->v[i]);
|
||||
+ FREE(table->parser, table->v);
|
||||
}
|
||||
|
||||
static void FASTCALL
|
||||
-hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) {
|
||||
+hashTableInit(HASH_TABLE *p, XML_Parser parser) {
|
||||
p->power = 0;
|
||||
p->size = 0;
|
||||
p->used = 0;
|
||||
p->v = NULL;
|
||||
- p->mem = ms;
|
||||
+ p->parser = parser;
|
||||
}
|
||||
|
||||
static void FASTCALL
|
||||
62
meta/recipes-core/expat/expat/CVE-2025-59375-04.patch
Normal file
62
meta/recipes-core/expat/expat/CVE-2025-59375-04.patch
Normal file
@@ -0,0 +1,62 @@
|
||||
From 51487ad9d760faa4809b0f8e189d2f666317e41a Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 1 Sep 2025 17:45:50 +0200
|
||||
Subject: [PATCH] lib: Make function copyString use macro MALLOC
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/51487ad9d760faa4809b0f8e189d2f666317e41a]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 11 +++++------
|
||||
1 file changed, 5 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index a65b0265..c0576abd 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -593,8 +593,7 @@ static XML_Content *build_model(XML_Parser parser);
|
||||
static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc,
|
||||
const char *ptr, const char *end);
|
||||
|
||||
-static XML_Char *copyString(const XML_Char *s,
|
||||
- const XML_Memory_Handling_Suite *memsuite);
|
||||
+static XML_Char *copyString(const XML_Char *s, XML_Parser parser);
|
||||
|
||||
static unsigned long generate_hash_secret_salt(XML_Parser parser);
|
||||
static XML_Bool startParsing(XML_Parser parser);
|
||||
@@ -1231,7 +1230,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
|
||||
parser->m_processor = prologInitProcessor;
|
||||
XmlPrologStateInit(&parser->m_prologState);
|
||||
if (encodingName != NULL) {
|
||||
- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));
|
||||
+ parser->m_protocolEncodingName = copyString(encodingName, parser);
|
||||
}
|
||||
parser->m_curBase = NULL;
|
||||
XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0);
|
||||
@@ -1419,7 +1418,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) {
|
||||
parser->m_protocolEncodingName = NULL;
|
||||
else {
|
||||
/* Copy the new encoding name into allocated memory */
|
||||
- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));
|
||||
+ parser->m_protocolEncodingName = copyString(encodingName, parser);
|
||||
if (! parser->m_protocolEncodingName)
|
||||
return XML_STATUS_ERROR;
|
||||
}
|
||||
@@ -8064,7 +8063,7 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr,
|
||||
}
|
||||
|
||||
static XML_Char *
|
||||
-copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
|
||||
+copyString(const XML_Char *s, XML_Parser parser) {
|
||||
size_t charsRequired = 0;
|
||||
XML_Char *result;
|
||||
|
||||
@@ -8076,7 +8075,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
|
||||
charsRequired++;
|
||||
|
||||
/* Now allocate space for the copy */
|
||||
- result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char));
|
||||
+ result = MALLOC(parser, charsRequired * sizeof(XML_Char));
|
||||
if (result == NULL)
|
||||
return NULL;
|
||||
/* Copy the original into place */
|
||||
64
meta/recipes-core/expat/expat/CVE-2025-59375-05.patch
Normal file
64
meta/recipes-core/expat/expat/CVE-2025-59375-05.patch
Normal file
@@ -0,0 +1,64 @@
|
||||
From b3f0bda5f5e979781469532f7c304f7e223568d5 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 1 Sep 2025 17:48:02 +0200
|
||||
Subject: [PATCH] lib: Make function dtdReset use macro FREE
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/b3f0bda5f5e979781469532f7c304f7e223568d5]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 12 ++++++------
|
||||
1 file changed, 6 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index c0576abd..65fcce30 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -557,7 +557,7 @@ static void FASTCALL normalizePublicId(XML_Char *s);
|
||||
|
||||
static DTD *dtdCreate(XML_Parser parser);
|
||||
/* do not call if m_parentParser != NULL */
|
||||
-static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms);
|
||||
+static void dtdReset(DTD *p, XML_Parser parser);
|
||||
static void dtdDestroy(DTD *p, XML_Bool isDocEntity,
|
||||
const XML_Memory_Handling_Suite *ms);
|
||||
static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
|
||||
@@ -1382,7 +1382,7 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) {
|
||||
FREE(parser, (void *)parser->m_protocolEncodingName);
|
||||
parser->m_protocolEncodingName = NULL;
|
||||
parserInit(parser, encodingName);
|
||||
- dtdReset(parser->m_dtd, &parser->m_mem);
|
||||
+ dtdReset(parser->m_dtd, parser);
|
||||
return XML_TRUE;
|
||||
}
|
||||
|
||||
@@ -7155,7 +7155,7 @@ dtdCreate(XML_Parser parser) {
|
||||
}
|
||||
|
||||
static void
|
||||
-dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
|
||||
+dtdReset(DTD *p, XML_Parser parser) {
|
||||
HASH_TABLE_ITER iter;
|
||||
hashTableIterInit(&iter, &(p->elementTypes));
|
||||
for (;;) {
|
||||
@@ -7163,7 +7163,7 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
|
||||
if (! e)
|
||||
break;
|
||||
if (e->allocDefaultAtts != 0)
|
||||
- ms->free_fcn(e->defaultAtts);
|
||||
+ FREE(parser, e->defaultAtts);
|
||||
}
|
||||
hashTableClear(&(p->generalEntities));
|
||||
#ifdef XML_DTD
|
||||
@@ -7180,9 +7180,9 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
|
||||
|
||||
p->in_eldecl = XML_FALSE;
|
||||
|
||||
- ms->free_fcn(p->scaffIndex);
|
||||
+ FREE(parser, p->scaffIndex);
|
||||
p->scaffIndex = NULL;
|
||||
- ms->free_fcn(p->scaffold);
|
||||
+ FREE(parser, p->scaffold);
|
||||
p->scaffold = NULL;
|
||||
|
||||
p->scaffLevel = 0;
|
||||
68
meta/recipes-core/expat/expat/CVE-2025-59375-06.patch
Normal file
68
meta/recipes-core/expat/expat/CVE-2025-59375-06.patch
Normal file
@@ -0,0 +1,68 @@
|
||||
From 53a3eda0ae2e0317afd071b72b41976053d82732 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 1 Sep 2025 17:50:59 +0200
|
||||
Subject: [PATCH] lib: Make function dtdDestroy use macro FREE
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/53a3eda0ae2e0317afd071b72b41976053d82732]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 16 +++++++---------
|
||||
1 file changed, 7 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 65fcce30..e7df97da 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -558,8 +558,7 @@ static void FASTCALL normalizePublicId(XML_Char *s);
|
||||
static DTD *dtdCreate(XML_Parser parser);
|
||||
/* do not call if m_parentParser != NULL */
|
||||
static void dtdReset(DTD *p, XML_Parser parser);
|
||||
-static void dtdDestroy(DTD *p, XML_Bool isDocEntity,
|
||||
- const XML_Memory_Handling_Suite *ms);
|
||||
+static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser);
|
||||
static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
|
||||
const XML_Memory_Handling_Suite *ms);
|
||||
static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable,
|
||||
@@ -1685,8 +1684,7 @@ XML_ParserFree(XML_Parser parser) {
|
||||
#else
|
||||
if (parser->m_dtd)
|
||||
#endif /* XML_DTD */
|
||||
- dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser,
|
||||
- &parser->m_mem);
|
||||
+ dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, parser);
|
||||
FREE(parser, (void *)parser->m_atts);
|
||||
#ifdef XML_ATTR_INFO
|
||||
FREE(parser, (void *)parser->m_attInfo);
|
||||
@@ -7196,7 +7194,7 @@ dtdReset(DTD *p, XML_Parser parser) {
|
||||
}
|
||||
|
||||
static void
|
||||
-dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
|
||||
+dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) {
|
||||
HASH_TABLE_ITER iter;
|
||||
hashTableIterInit(&iter, &(p->elementTypes));
|
||||
for (;;) {
|
||||
@@ -7204,7 +7202,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
|
||||
if (! e)
|
||||
break;
|
||||
if (e->allocDefaultAtts != 0)
|
||||
- ms->free_fcn(e->defaultAtts);
|
||||
+ FREE(parser, e->defaultAtts);
|
||||
}
|
||||
hashTableDestroy(&(p->generalEntities));
|
||||
#ifdef XML_DTD
|
||||
@@ -7216,10 +7214,10 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
|
||||
poolDestroy(&(p->pool));
|
||||
poolDestroy(&(p->entityValuePool));
|
||||
if (isDocEntity) {
|
||||
- ms->free_fcn(p->scaffIndex);
|
||||
- ms->free_fcn(p->scaffold);
|
||||
+ FREE(parser, p->scaffIndex);
|
||||
+ FREE(parser, p->scaffold);
|
||||
}
|
||||
- ms->free_fcn(p);
|
||||
+ FREE(parser, p);
|
||||
}
|
||||
|
||||
/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise.
|
||||
52
meta/recipes-core/expat/expat/CVE-2025-59375-07.patch
Normal file
52
meta/recipes-core/expat/expat/CVE-2025-59375-07.patch
Normal file
@@ -0,0 +1,52 @@
|
||||
From 4e7a5d03daf672f20c73d40dc8970385c18b30d3 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 1 Sep 2025 17:52:58 +0200
|
||||
Subject: [PATCH] lib: Make function dtdCopy use macro MALLOC
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4e7a5d03daf672f20c73d40dc8970385c18b30d3]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index e7df97da..9f0a8b3e 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -560,7 +560,7 @@ static DTD *dtdCreate(XML_Parser parser);
|
||||
static void dtdReset(DTD *p, XML_Parser parser);
|
||||
static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser);
|
||||
static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
|
||||
- const XML_Memory_Handling_Suite *ms);
|
||||
+ XML_Parser parser);
|
||||
static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable,
|
||||
STRING_POOL *newPool, const HASH_TABLE *oldTable);
|
||||
static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name,
|
||||
@@ -1572,7 +1572,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
|
||||
parser->m_prologState.inEntityValue = oldInEntityValue;
|
||||
if (context) {
|
||||
#endif /* XML_DTD */
|
||||
- if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem)
|
||||
+ if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, parser)
|
||||
|| ! setContext(parser, context)) {
|
||||
XML_ParserFree(parser);
|
||||
return NULL;
|
||||
@@ -7225,7 +7225,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) {
|
||||
*/
|
||||
static int
|
||||
dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
|
||||
- const XML_Memory_Handling_Suite *ms) {
|
||||
+ XML_Parser parser) {
|
||||
HASH_TABLE_ITER iter;
|
||||
|
||||
/* Copy the prefix table. */
|
||||
@@ -7306,7 +7306,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
|
||||
}
|
||||
#endif
|
||||
newE->defaultAtts
|
||||
- = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
|
||||
+ = MALLOC(parser, oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
|
||||
if (! newE->defaultAtts) {
|
||||
return 0;
|
||||
}
|
||||
577
meta/recipes-core/expat/expat/CVE-2025-59375-08.patch
Normal file
577
meta/recipes-core/expat/expat/CVE-2025-59375-08.patch
Normal file
@@ -0,0 +1,577 @@
|
||||
From cfce28e171676fe6f70d17b97ed8a59eaeb83f15 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 1 Sep 2025 17:34:58 +0200
|
||||
Subject: [PATCH] lib: Implement tracking of dynamic memory allocations
|
||||
|
||||
**PLEASE NOTE** that distributors intending to backport (or cherry-pick)
|
||||
this fix need to copy 99% of the related pull request, not just this
|
||||
commit, to not end up with a state that literally does both too much and
|
||||
too little at the same time. Appending ".diff" to the pull request URL
|
||||
could be of help.
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/cfce28e171676fe6f70d17b97ed8a59eaeb83f15]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/expat.h | 15 +-
|
||||
lib/internal.h | 5 +
|
||||
lib/libexpat.def.cmake | 3 +
|
||||
lib/xmlparse.c | 337 +++++++++++++++++++++++++++++++++++++++--
|
||||
tests/basic_tests.c | 4 +
|
||||
tests/nsalloc_tests.c | 5 +
|
||||
xmlwf/xmlwf.c | 2 +
|
||||
xmlwf/xmlwf_helpgen.py | 2 +
|
||||
8 files changed, 361 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/lib/expat.h b/lib/expat.h
|
||||
index 610e1ddc..66a253c1 100644
|
||||
--- a/lib/expat.h
|
||||
+++ b/lib/expat.h
|
||||
@@ -1032,7 +1032,10 @@ enum XML_FeatureEnum {
|
||||
XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
|
||||
XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT,
|
||||
/* Added in Expat 2.6.0. */
|
||||
- XML_FEATURE_GE
|
||||
+ XML_FEATURE_GE,
|
||||
+ /* Added in Expat 2.7.2. */
|
||||
+ XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT,
|
||||
+ XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT,
|
||||
/* Additional features must be added to the end of this enum. */
|
||||
};
|
||||
|
||||
@@ -1057,6 +1060,16 @@ XML_SetBillionLaughsAttackProtectionMaximumAmplification(
|
||||
XMLPARSEAPI(XML_Bool)
|
||||
XML_SetBillionLaughsAttackProtectionActivationThreshold(
|
||||
XML_Parser parser, unsigned long long activationThresholdBytes);
|
||||
+
|
||||
+/* Added in Expat 2.7.2. */
|
||||
+XMLPARSEAPI(XML_Bool)
|
||||
+XML_SetAllocTrackerMaximumAmplification(XML_Parser parser,
|
||||
+ float maximumAmplificationFactor);
|
||||
+
|
||||
+/* Added in Expat 2.7.2. */
|
||||
+XMLPARSEAPI(XML_Bool)
|
||||
+XML_SetAllocTrackerActivationThreshold(
|
||||
+ XML_Parser parser, unsigned long long activationThresholdBytes);
|
||||
#endif
|
||||
|
||||
/* Added in Expat 2.6.0. */
|
||||
diff --git a/lib/internal.h b/lib/internal.h
|
||||
index 6bde6ae6..eb67cf50 100644
|
||||
--- a/lib/internal.h
|
||||
+++ b/lib/internal.h
|
||||
@@ -145,6 +145,11 @@
|
||||
100.0f
|
||||
#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \
|
||||
8388608 // 8 MiB, 2^23
|
||||
+
|
||||
+#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f
|
||||
+#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \
|
||||
+ 67108864 // 64 MiB, 2^26
|
||||
+
|
||||
/* NOTE END */
|
||||
|
||||
#include "expat.h" // so we can use type XML_Parser below
|
||||
diff --git a/lib/libexpat.def.cmake b/lib/libexpat.def.cmake
|
||||
index 10ee9cd6..7a3a7ec0 100644
|
||||
--- a/lib/libexpat.def.cmake
|
||||
+++ b/lib/libexpat.def.cmake
|
||||
@@ -79,3 +79,6 @@ EXPORTS
|
||||
@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
|
||||
; added with version 2.6.0
|
||||
XML_SetReparseDeferralEnabled @71
|
||||
+; added with version 2.7.2
|
||||
+@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerMaximumAmplification @72
|
||||
+@_EXPAT_COMMENT_DTD_OR_GE@ XML_SetAllocTrackerActivationThreshold @73
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 9f0a8b3e..fcf1cfdd 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -452,6 +452,14 @@ typedef struct accounting {
|
||||
unsigned long long activationThresholdBytes;
|
||||
} ACCOUNTING;
|
||||
|
||||
+typedef struct MALLOC_TRACKER {
|
||||
+ XmlBigCount bytesAllocated;
|
||||
+ XmlBigCount peakBytesAllocated; // updated live only for debug level >=2
|
||||
+ unsigned long debugLevel;
|
||||
+ float maximumAmplificationFactor; // >=1.0
|
||||
+ XmlBigCount activationThresholdBytes;
|
||||
+} MALLOC_TRACKER;
|
||||
+
|
||||
typedef struct entity_stats {
|
||||
unsigned int countEverOpened;
|
||||
unsigned int currentDepth;
|
||||
@@ -599,7 +607,8 @@ static XML_Bool startParsing(XML_Parser parser);
|
||||
|
||||
static XML_Parser parserCreate(const XML_Char *encodingName,
|
||||
const XML_Memory_Handling_Suite *memsuite,
|
||||
- const XML_Char *nameSep, DTD *dtd);
|
||||
+ const XML_Char *nameSep, DTD *dtd,
|
||||
+ XML_Parser parentParser);
|
||||
|
||||
static void parserInit(XML_Parser parser, const XML_Char *encodingName);
|
||||
|
||||
@@ -769,14 +778,220 @@ struct XML_ParserStruct {
|
||||
unsigned long m_hash_secret_salt;
|
||||
#if XML_GE == 1
|
||||
ACCOUNTING m_accounting;
|
||||
+ MALLOC_TRACKER m_alloc_tracker;
|
||||
ENTITY_STATS m_entity_stats;
|
||||
#endif
|
||||
XML_Bool m_reenter;
|
||||
};
|
||||
|
||||
-#define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
|
||||
-#define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s)))
|
||||
-#define FREE(parser, p) (parser->m_mem.free_fcn((p)))
|
||||
+#if XML_GE == 1
|
||||
+# define MALLOC(parser, s) (expat_malloc((parser), (s), __LINE__))
|
||||
+# define REALLOC(parser, p, s) (expat_realloc((parser), (p), (s), __LINE__))
|
||||
+# define FREE(parser, p) (expat_free((parser), (p), __LINE__))
|
||||
+#else
|
||||
+# define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
|
||||
+# define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s)))
|
||||
+# define FREE(parser, p) (parser->m_mem.free_fcn((p)))
|
||||
+#endif
|
||||
+
|
||||
+#if XML_GE == 1
|
||||
+static void
|
||||
+expat_heap_stat(XML_Parser rootParser, char operator, XmlBigCount absDiff,
|
||||
+ XmlBigCount newTotal, XmlBigCount peakTotal, int sourceLine) {
|
||||
+ // NOTE: This can be +infinity or -nan
|
||||
+ const float amplification
|
||||
+ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect;
|
||||
+ fprintf(
|
||||
+ stderr,
|
||||
+ "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" EXPAT_FMT_ULL(
|
||||
+ "10") " to " EXPAT_FMT_ULL("10") " (" EXPAT_FMT_ULL("10") " peak), amplification %8.2f (xmlparse.c:%d)\n",
|
||||
+ (void *)rootParser, rootParser->m_accounting.countBytesDirect, operator,
|
||||
+ absDiff, newTotal, peakTotal, (double)amplification, sourceLine);
|
||||
+}
|
||||
+
|
||||
+static bool
|
||||
+expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase,
|
||||
+ int sourceLine) {
|
||||
+ assert(rootParser != NULL);
|
||||
+ assert(increase > 0);
|
||||
+
|
||||
+ XmlBigCount newTotal = 0;
|
||||
+ bool tolerable = true;
|
||||
+
|
||||
+ // Detect integer overflow
|
||||
+ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated < increase) {
|
||||
+ tolerable = false;
|
||||
+ } else {
|
||||
+ newTotal = rootParser->m_alloc_tracker.bytesAllocated + increase;
|
||||
+
|
||||
+ if (newTotal >= rootParser->m_alloc_tracker.activationThresholdBytes) {
|
||||
+ assert(newTotal > 0);
|
||||
+ // NOTE: This can be +infinity when dividing by zero but not -nan
|
||||
+ const float amplification
|
||||
+ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect;
|
||||
+ if (amplification
|
||||
+ > rootParser->m_alloc_tracker.maximumAmplificationFactor) {
|
||||
+ tolerable = false;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (! tolerable && (rootParser->m_alloc_tracker.debugLevel >= 1)) {
|
||||
+ expat_heap_stat(rootParser, '+', increase, newTotal, newTotal, sourceLine);
|
||||
+ }
|
||||
+
|
||||
+ return tolerable;
|
||||
+}
|
||||
+
|
||||
+static void *
|
||||
+expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
|
||||
+ // Detect integer overflow
|
||||
+ if (SIZE_MAX - size < sizeof(size_t)) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
|
||||
+ assert(rootParser->m_parentParser == NULL);
|
||||
+
|
||||
+ const size_t bytesToAllocate = sizeof(size_t) + size;
|
||||
+
|
||||
+ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated
|
||||
+ < bytesToAllocate) {
|
||||
+ return NULL; // i.e. signal integer overflow as out-of-memory
|
||||
+ }
|
||||
+
|
||||
+ if (! expat_heap_increase_tolerable(rootParser, bytesToAllocate,
|
||||
+ sourceLine)) {
|
||||
+ return NULL; // i.e. signal violation as out-of-memory
|
||||
+ }
|
||||
+
|
||||
+ // Actually allocate
|
||||
+ void *const mallocedPtr = parser->m_mem.malloc_fcn(bytesToAllocate);
|
||||
+
|
||||
+ if (mallocedPtr == NULL) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ // Update in-block recorded size
|
||||
+ *(size_t *)mallocedPtr = size;
|
||||
+
|
||||
+ // Update accounting
|
||||
+ rootParser->m_alloc_tracker.bytesAllocated += bytesToAllocate;
|
||||
+
|
||||
+ // Report as needed
|
||||
+ if (rootParser->m_alloc_tracker.debugLevel >= 2) {
|
||||
+ if (rootParser->m_alloc_tracker.bytesAllocated
|
||||
+ > rootParser->m_alloc_tracker.peakBytesAllocated) {
|
||||
+ rootParser->m_alloc_tracker.peakBytesAllocated
|
||||
+ = rootParser->m_alloc_tracker.bytesAllocated;
|
||||
+ }
|
||||
+ expat_heap_stat(rootParser, '+', bytesToAllocate,
|
||||
+ rootParser->m_alloc_tracker.bytesAllocated,
|
||||
+ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
|
||||
+ }
|
||||
+
|
||||
+ return (char *)mallocedPtr + sizeof(size_t);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+expat_free(XML_Parser parser, void *ptr, int sourceLine) {
|
||||
+ assert(parser != NULL);
|
||||
+
|
||||
+ if (ptr == NULL) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
|
||||
+ assert(rootParser->m_parentParser == NULL);
|
||||
+
|
||||
+ // Extract size (to the eyes of malloc_fcn/realloc_fcn) and
|
||||
+ // the original pointer returned by malloc/realloc
|
||||
+ void *const mallocedPtr = (char *)ptr - sizeof(size_t);
|
||||
+ const size_t bytesAllocated = sizeof(size_t) + *(size_t *)mallocedPtr;
|
||||
+
|
||||
+ // Update accounting
|
||||
+ assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated);
|
||||
+ rootParser->m_alloc_tracker.bytesAllocated -= bytesAllocated;
|
||||
+
|
||||
+ // Report as needed
|
||||
+ if (rootParser->m_alloc_tracker.debugLevel >= 2) {
|
||||
+ expat_heap_stat(rootParser, '-', bytesAllocated,
|
||||
+ rootParser->m_alloc_tracker.bytesAllocated,
|
||||
+ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
|
||||
+ }
|
||||
+
|
||||
+ // NOTE: This may be freeing rootParser, so freeing has to come last
|
||||
+ parser->m_mem.free_fcn(mallocedPtr);
|
||||
+}
|
||||
+
|
||||
+static void *
|
||||
+expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
|
||||
+ assert(parser != NULL);
|
||||
+
|
||||
+ if (ptr == NULL) {
|
||||
+ return expat_malloc(parser, size, sourceLine);
|
||||
+ }
|
||||
+
|
||||
+ if (size == 0) {
|
||||
+ expat_free(parser, ptr, sourceLine);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
|
||||
+ assert(rootParser->m_parentParser == NULL);
|
||||
+
|
||||
+ // Extract original size (to the eyes of the caller) and the original
|
||||
+ // pointer returned by malloc/realloc
|
||||
+ void *mallocedPtr = (char *)ptr - sizeof(size_t);
|
||||
+ const size_t prevSize = *(size_t *)mallocedPtr;
|
||||
+
|
||||
+ // Classify upcoming change
|
||||
+ const bool isIncrease = (size > prevSize);
|
||||
+ const size_t absDiff
|
||||
+ = (size > prevSize) ? (size - prevSize) : (prevSize - size);
|
||||
+
|
||||
+ // Ask for permission from accounting
|
||||
+ if (isIncrease) {
|
||||
+ if (! expat_heap_increase_tolerable(rootParser, absDiff, sourceLine)) {
|
||||
+ return NULL; // i.e. signal violation as out-of-memory
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Actually allocate
|
||||
+ mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + size);
|
||||
+
|
||||
+ if (mallocedPtr == NULL) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
+
|
||||
+ // Update accounting
|
||||
+ if (isIncrease) {
|
||||
+ assert((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated
|
||||
+ >= absDiff);
|
||||
+ rootParser->m_alloc_tracker.bytesAllocated += absDiff;
|
||||
+ } else { // i.e. decrease
|
||||
+ assert(rootParser->m_alloc_tracker.bytesAllocated >= absDiff);
|
||||
+ rootParser->m_alloc_tracker.bytesAllocated -= absDiff;
|
||||
+ }
|
||||
+
|
||||
+ // Report as needed
|
||||
+ if (rootParser->m_alloc_tracker.debugLevel >= 2) {
|
||||
+ if (rootParser->m_alloc_tracker.bytesAllocated
|
||||
+ > rootParser->m_alloc_tracker.peakBytesAllocated) {
|
||||
+ rootParser->m_alloc_tracker.peakBytesAllocated
|
||||
+ = rootParser->m_alloc_tracker.bytesAllocated;
|
||||
+ }
|
||||
+ expat_heap_stat(rootParser, isIncrease ? '+' : '-', absDiff,
|
||||
+ rootParser->m_alloc_tracker.bytesAllocated,
|
||||
+ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
|
||||
+ }
|
||||
+
|
||||
+ // Update in-block recorded size
|
||||
+ *(size_t *)mallocedPtr = size;
|
||||
+
|
||||
+ return (char *)mallocedPtr + sizeof(size_t);
|
||||
+}
|
||||
+#endif // XML_GE == 1
|
||||
|
||||
XML_Parser XMLCALL
|
||||
XML_ParserCreate(const XML_Char *encodingName) {
|
||||
@@ -1096,19 +1311,40 @@ XML_Parser XMLCALL
|
||||
XML_ParserCreate_MM(const XML_Char *encodingName,
|
||||
const XML_Memory_Handling_Suite *memsuite,
|
||||
const XML_Char *nameSep) {
|
||||
- return parserCreate(encodingName, memsuite, nameSep, NULL);
|
||||
+ return parserCreate(encodingName, memsuite, nameSep, NULL, NULL);
|
||||
}
|
||||
|
||||
static XML_Parser
|
||||
parserCreate(const XML_Char *encodingName,
|
||||
const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep,
|
||||
- DTD *dtd) {
|
||||
- XML_Parser parser;
|
||||
+ DTD *dtd, XML_Parser parentParser) {
|
||||
+ XML_Parser parser = NULL;
|
||||
+
|
||||
+#if XML_GE == 1
|
||||
+ const size_t increase = sizeof(size_t) + sizeof(struct XML_ParserStruct);
|
||||
+
|
||||
+ if (parentParser != NULL) {
|
||||
+ const XML_Parser rootParser = getRootParserOf(parentParser, NULL);
|
||||
+ if (! expat_heap_increase_tolerable(rootParser, increase, __LINE__)) {
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ }
|
||||
+#else
|
||||
+ UNUSED_P(parentParser);
|
||||
+#endif
|
||||
|
||||
if (memsuite) {
|
||||
XML_Memory_Handling_Suite *mtemp;
|
||||
+#if XML_GE == 1
|
||||
+ void *const sizeAndParser = memsuite->malloc_fcn(
|
||||
+ sizeof(size_t) + sizeof(struct XML_ParserStruct));
|
||||
+ if (sizeAndParser != NULL) {
|
||||
+ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
|
||||
+ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t));
|
||||
+#else
|
||||
parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
|
||||
if (parser != NULL) {
|
||||
+#endif
|
||||
mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
|
||||
mtemp->malloc_fcn = memsuite->malloc_fcn;
|
||||
mtemp->realloc_fcn = memsuite->realloc_fcn;
|
||||
@@ -1116,18 +1352,67 @@ parserCreate(const XML_Char *encodingName,
|
||||
}
|
||||
} else {
|
||||
XML_Memory_Handling_Suite *mtemp;
|
||||
+#if XML_GE == 1
|
||||
+ void *const sizeAndParser
|
||||
+ = (XML_Parser)malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct));
|
||||
+ if (sizeAndParser != NULL) {
|
||||
+ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
|
||||
+ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t));
|
||||
+#else
|
||||
parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct));
|
||||
if (parser != NULL) {
|
||||
+#endif
|
||||
mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
|
||||
mtemp->malloc_fcn = malloc;
|
||||
mtemp->realloc_fcn = realloc;
|
||||
mtemp->free_fcn = free;
|
||||
}
|
||||
- }
|
||||
+ } // cppcheck-suppress[memleak symbolName=sizeAndParser] // Cppcheck >=2.18.0
|
||||
|
||||
if (! parser)
|
||||
return parser;
|
||||
|
||||
+#if XML_GE == 1
|
||||
+ // Initialize .m_alloc_tracker
|
||||
+ memset(&parser->m_alloc_tracker, 0, sizeof(MALLOC_TRACKER));
|
||||
+ if (parentParser == NULL) {
|
||||
+ parser->m_alloc_tracker.debugLevel
|
||||
+ = getDebugLevel("EXPAT_MALLOC_DEBUG", 0u);
|
||||
+ parser->m_alloc_tracker.maximumAmplificationFactor
|
||||
+ = EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT;
|
||||
+ parser->m_alloc_tracker.activationThresholdBytes
|
||||
+ = EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT;
|
||||
+
|
||||
+ // NOTE: This initialization needs to come this early because these fields
|
||||
+ // are read by allocation tracking code
|
||||
+ parser->m_parentParser = NULL;
|
||||
+ parser->m_accounting.countBytesDirect = 0;
|
||||
+ } else {
|
||||
+ parser->m_parentParser = parentParser;
|
||||
+ }
|
||||
+
|
||||
+ // Record XML_ParserStruct allocation we did a few lines up before
|
||||
+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
|
||||
+ assert(rootParser->m_parentParser == NULL);
|
||||
+ assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= increase);
|
||||
+ rootParser->m_alloc_tracker.bytesAllocated += increase;
|
||||
+
|
||||
+ // Report on allocation
|
||||
+ if (rootParser->m_alloc_tracker.debugLevel >= 2) {
|
||||
+ if (rootParser->m_alloc_tracker.bytesAllocated
|
||||
+ > rootParser->m_alloc_tracker.peakBytesAllocated) {
|
||||
+ rootParser->m_alloc_tracker.peakBytesAllocated
|
||||
+ = rootParser->m_alloc_tracker.bytesAllocated;
|
||||
+ }
|
||||
+
|
||||
+ expat_heap_stat(rootParser, '+', increase,
|
||||
+ rootParser->m_alloc_tracker.bytesAllocated,
|
||||
+ rootParser->m_alloc_tracker.peakBytesAllocated, __LINE__);
|
||||
+ }
|
||||
+#else
|
||||
+ parser->m_parentParser = NULL;
|
||||
+#endif // XML_GE == 1
|
||||
+
|
||||
parser->m_buffer = NULL;
|
||||
parser->m_bufferLim = NULL;
|
||||
|
||||
@@ -1291,7 +1576,6 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
|
||||
parser->m_unknownEncodingMem = NULL;
|
||||
parser->m_unknownEncodingRelease = NULL;
|
||||
parser->m_unknownEncodingData = NULL;
|
||||
- parser->m_parentParser = NULL;
|
||||
parser->m_parsingStatus.parsing = XML_INITIALIZED;
|
||||
// Reentry can only be triggered inside m_processor calls
|
||||
parser->m_reenter = XML_FALSE;
|
||||
@@ -1526,9 +1810,10 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
|
||||
*/
|
||||
if (parser->m_ns) {
|
||||
XML_Char tmp[2] = {parser->m_namespaceSeparator, 0};
|
||||
- parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
|
||||
+ parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd, oldParser);
|
||||
} else {
|
||||
- parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
|
||||
+ parser
|
||||
+ = parserCreate(encodingName, &parser->m_mem, NULL, newDtd, oldParser);
|
||||
}
|
||||
|
||||
if (! parser)
|
||||
@@ -2708,6 +2993,13 @@ XML_GetFeatureList(void) {
|
||||
EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT},
|
||||
/* Added in Expat 2.6.0. */
|
||||
{XML_FEATURE_GE, XML_L("XML_GE"), 0},
|
||||
+ /* Added in Expat 2.7.2. */
|
||||
+ {XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT,
|
||||
+ XML_L("XML_AT_MAX_AMP"),
|
||||
+ (long int)EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT},
|
||||
+ {XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT,
|
||||
+ XML_L("XML_AT_ACT_THRES"),
|
||||
+ (long int)EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT},
|
||||
#endif
|
||||
{XML_FEATURE_END, NULL, 0}};
|
||||
|
||||
@@ -2736,6 +3028,29 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
|
||||
parser->m_accounting.activationThresholdBytes = activationThresholdBytes;
|
||||
return XML_TRUE;
|
||||
}
|
||||
+
|
||||
+XML_Bool XMLCALL
|
||||
+XML_SetAllocTrackerMaximumAmplification(XML_Parser parser,
|
||||
+ float maximumAmplificationFactor) {
|
||||
+ if ((parser == NULL) || (parser->m_parentParser != NULL)
|
||||
+ || isnan(maximumAmplificationFactor)
|
||||
+ || (maximumAmplificationFactor < 1.0f)) {
|
||||
+ return XML_FALSE;
|
||||
+ }
|
||||
+ parser->m_alloc_tracker.maximumAmplificationFactor
|
||||
+ = maximumAmplificationFactor;
|
||||
+ return XML_TRUE;
|
||||
+}
|
||||
+
|
||||
+XML_Bool XMLCALL
|
||||
+XML_SetAllocTrackerActivationThreshold(
|
||||
+ XML_Parser parser, unsigned long long activationThresholdBytes) {
|
||||
+ if ((parser == NULL) || (parser->m_parentParser != NULL)) {
|
||||
+ return XML_FALSE;
|
||||
+ }
|
||||
+ parser->m_alloc_tracker.activationThresholdBytes = activationThresholdBytes;
|
||||
+ return XML_TRUE;
|
||||
+}
|
||||
#endif /* XML_GE == 1 */
|
||||
|
||||
XML_Bool XMLCALL
|
||||
diff --git a/tests/basic_tests.c b/tests/basic_tests.c
|
||||
index 129db1d8..0231e094 100644
|
||||
--- a/tests/basic_tests.c
|
||||
+++ b/tests/basic_tests.c
|
||||
@@ -3089,6 +3089,10 @@ START_TEST(test_buffer_can_grow_to_max) {
|
||||
for (int i = 0; i < num_prefixes; ++i) {
|
||||
set_subtest("\"%s\"", prefixes[i]);
|
||||
XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+#if XML_GE == 1
|
||||
+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, (size_t)-1)
|
||||
+ == XML_TRUE); // i.e. deactivate
|
||||
+#endif
|
||||
const int prefix_len = (int)strlen(prefixes[i]);
|
||||
const enum XML_Status s
|
||||
= _XML_Parse_SINGLE_BYTES(parser, prefixes[i], prefix_len, XML_FALSE);
|
||||
diff --git a/tests/nsalloc_tests.c b/tests/nsalloc_tests.c
|
||||
index 48520f42..0a594e14 100644
|
||||
--- a/tests/nsalloc_tests.c
|
||||
+++ b/tests/nsalloc_tests.c
|
||||
@@ -454,10 +454,15 @@ START_TEST(test_nsalloc_realloc_attributes) {
|
||||
nsalloc_teardown();
|
||||
nsalloc_setup();
|
||||
}
|
||||
+#if XML_GE == 1
|
||||
+ assert_true(
|
||||
+ i == 0); // because expat_realloc relies on expat_malloc to some extent
|
||||
+#else
|
||||
if (i == 0)
|
||||
fail("Parsing worked despite failing reallocations");
|
||||
else if (i == max_realloc_count)
|
||||
fail("Parsing failed at max reallocation count");
|
||||
+#endif
|
||||
}
|
||||
END_TEST
|
||||
|
||||
diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
|
||||
index 8cfc73ca..b9d0a7fc 100644
|
||||
--- a/xmlwf/xmlwf.c
|
||||
+++ b/xmlwf/xmlwf.c
|
||||
@@ -933,6 +933,8 @@ usage(const XML_Char *prog, int rc) {
|
||||
T(" Control verbosity of entity debugging (default: 0)\n")
|
||||
T(" EXPAT_ENTROPY_DEBUG=(0|1)\n")
|
||||
T(" Control verbosity of entropy debugging (default: 0)\n")
|
||||
+ T(" EXPAT_MALLOC_DEBUG=(0|1|2)\n")
|
||||
+ T(" Control verbosity of allocation tracker (default: 0)\n")
|
||||
T("\n")
|
||||
T("exit status:\n")
|
||||
T(" 0 the input files are well-formed and the output (if requested) was written successfully\n")
|
||||
diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py
|
||||
index 39a3dc13..2360820d 100755
|
||||
--- a/xmlwf/xmlwf_helpgen.py
|
||||
+++ b/xmlwf/xmlwf_helpgen.py
|
||||
@@ -39,6 +39,8 @@ environment variables:
|
||||
Control verbosity of entity debugging (default: 0)
|
||||
EXPAT_ENTROPY_DEBUG=(0|1)
|
||||
Control verbosity of entropy debugging (default: 0)
|
||||
+ EXPAT_MALLOC_DEBUG=(0|1|2)
|
||||
+ Control verbosity of allocation tracker (default: 0)
|
||||
|
||||
exit status:
|
||||
0 the input files are well-formed and the output (if requested) was written successfully
|
||||
43
meta/recipes-core/expat/expat/CVE-2025-59375-09.patch
Normal file
43
meta/recipes-core/expat/expat/CVE-2025-59375-09.patch
Normal file
@@ -0,0 +1,43 @@
|
||||
From 1270e5bc0836d296ac4970fc9e1cf53d83972083 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Sun, 7 Sep 2025 12:18:08 +0200
|
||||
Subject: [PATCH] lib: Make XML_MemFree and XML_FreeContentModel match their
|
||||
siblings
|
||||
|
||||
.. XML_MemMalloc and XML_MemRealloc in structure, prior to upcoming changes
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/1270e5bc0836d296ac4970fc9e1cf53d83972083]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 10 ++++++----
|
||||
1 file changed, 6 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index fcf1cfdd..5d27cd45 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -2772,8 +2772,9 @@ XML_GetCurrentColumnNumber(XML_Parser parser) {
|
||||
|
||||
void XMLCALL
|
||||
XML_FreeContentModel(XML_Parser parser, XML_Content *model) {
|
||||
- if (parser != NULL)
|
||||
- FREE(parser, model);
|
||||
+ if (parser == NULL)
|
||||
+ return;
|
||||
+ FREE(parser, model);
|
||||
}
|
||||
|
||||
void *XMLCALL
|
||||
@@ -2792,8 +2793,9 @@ XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) {
|
||||
|
||||
void XMLCALL
|
||||
XML_MemFree(XML_Parser parser, void *ptr) {
|
||||
- if (parser != NULL)
|
||||
- FREE(parser, ptr);
|
||||
+ if (parser == NULL)
|
||||
+ return;
|
||||
+ FREE(parser, ptr);
|
||||
}
|
||||
|
||||
void XMLCALL
|
||||
54
meta/recipes-core/expat/expat/CVE-2025-59375-10.patch
Normal file
54
meta/recipes-core/expat/expat/CVE-2025-59375-10.patch
Normal file
@@ -0,0 +1,54 @@
|
||||
From 96c7467281c72028aada525c1d3822512758b266 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Sun, 7 Sep 2025 12:06:43 +0200
|
||||
Subject: [PATCH] lib: Exclude XML_Mem* functions from allocation tracking
|
||||
|
||||
.. so that allocations by the user application
|
||||
are not being limited.
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/96c7467281c72028aada525c1d3822512758b266]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 16 +++++++++++++---
|
||||
1 file changed, 13 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 5d27cd45..8145a049 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -2781,21 +2781,31 @@ void *XMLCALL
|
||||
XML_MemMalloc(XML_Parser parser, size_t size) {
|
||||
if (parser == NULL)
|
||||
return NULL;
|
||||
- return MALLOC(parser, size);
|
||||
+
|
||||
+ // NOTE: We are avoiding MALLOC(..) here to not include
|
||||
+ // user allocations with allocation tracking and limiting.
|
||||
+ return parser->m_mem.malloc_fcn(size);
|
||||
}
|
||||
|
||||
void *XMLCALL
|
||||
XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) {
|
||||
if (parser == NULL)
|
||||
return NULL;
|
||||
- return REALLOC(parser, ptr, size);
|
||||
+
|
||||
+ // NOTE: We are avoiding REALLOC(..) here to not include
|
||||
+ // user allocations with allocation tracking and limiting.
|
||||
+ return parser->m_mem.realloc_fcn(ptr, size);
|
||||
}
|
||||
|
||||
void XMLCALL
|
||||
XML_MemFree(XML_Parser parser, void *ptr) {
|
||||
if (parser == NULL)
|
||||
return;
|
||||
- FREE(parser, ptr);
|
||||
+
|
||||
+ // NOTE: We are avoiding FREE(..) here because XML_MemMalloc and
|
||||
+ // XML_MemRealloc are not using MALLOC(..) and REALLOC(..)
|
||||
+ // but plain .malloc_fcn(..) and .realloc_fcn(..), internally.
|
||||
+ parser->m_mem.free_fcn(ptr);
|
||||
}
|
||||
|
||||
void XMLCALL
|
||||
66
meta/recipes-core/expat/expat/CVE-2025-59375-11.patch
Normal file
66
meta/recipes-core/expat/expat/CVE-2025-59375-11.patch
Normal file
@@ -0,0 +1,66 @@
|
||||
From ae4086198d710a62a0a1560007b81307dba72909 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Tue, 9 Sep 2025 21:34:28 +0200
|
||||
Subject: [PATCH] lib: Exclude the main input buffer from allocation tracking
|
||||
|
||||
.. so that control of the input buffer size remains with the
|
||||
application using Expat
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/ae4086198d710a62a0a1560007b81307dba72909]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 19 +++++++++++++++----
|
||||
1 file changed, 15 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 8145a049..00139b94 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -1975,7 +1975,10 @@ XML_ParserFree(XML_Parser parser) {
|
||||
FREE(parser, (void *)parser->m_attInfo);
|
||||
#endif
|
||||
FREE(parser, parser->m_groupConnector);
|
||||
- FREE(parser, parser->m_buffer);
|
||||
+ // NOTE: We are avoiding FREE(..) here because parser->m_buffer
|
||||
+ // is not being allocated with MALLOC(..) but with plain
|
||||
+ // .malloc_fcn(..).
|
||||
+ parser->m_mem.free_fcn(parser->m_buffer);
|
||||
FREE(parser, parser->m_dataBuf);
|
||||
FREE(parser, parser->m_nsAtts);
|
||||
FREE(parser, parser->m_unknownEncodingMem);
|
||||
@@ -2567,7 +2570,9 @@ XML_GetBuffer(XML_Parser parser, int len) {
|
||||
parser->m_errorCode = XML_ERROR_NO_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
- newBuf = (char *)MALLOC(parser, bufferSize);
|
||||
+ // NOTE: We are avoiding MALLOC(..) here to leave limiting
|
||||
+ // the input size to the application using Expat.
|
||||
+ newBuf = (char *)parser->m_mem.malloc_fcn(bufferSize);
|
||||
if (newBuf == 0) {
|
||||
parser->m_errorCode = XML_ERROR_NO_MEMORY;
|
||||
return NULL;
|
||||
@@ -2578,7 +2583,10 @@ XML_GetBuffer(XML_Parser parser, int len) {
|
||||
memcpy(newBuf, &parser->m_bufferPtr[-keep],
|
||||
EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
|
||||
+ keep);
|
||||
- FREE(parser, parser->m_buffer);
|
||||
+ // NOTE: We are avoiding FREE(..) here because parser->m_buffer
|
||||
+ // is not being allocated with MALLOC(..) but with plain
|
||||
+ // .malloc_fcn(..).
|
||||
+ parser->m_mem.free_fcn(parser->m_buffer);
|
||||
parser->m_buffer = newBuf;
|
||||
parser->m_bufferEnd
|
||||
= parser->m_buffer
|
||||
@@ -2594,7 +2602,10 @@ XML_GetBuffer(XML_Parser parser, int len) {
|
||||
if (parser->m_bufferPtr) {
|
||||
memcpy(newBuf, parser->m_bufferPtr,
|
||||
EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
|
||||
- FREE(parser, parser->m_buffer);
|
||||
+ // NOTE: We are avoiding FREE(..) here because parser->m_buffer
|
||||
+ // is not being allocated with MALLOC(..) but with plain
|
||||
+ // .malloc_fcn(..).
|
||||
+ parser->m_mem.free_fcn(parser->m_buffer);
|
||||
parser->m_bufferEnd
|
||||
= newBuf
|
||||
+ EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
|
||||
58
meta/recipes-core/expat/expat/CVE-2025-59375-12.patch
Normal file
58
meta/recipes-core/expat/expat/CVE-2025-59375-12.patch
Normal file
@@ -0,0 +1,58 @@
|
||||
From 7e35240dc97e9fd4f609e31f27c27b659535e436 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Thu, 11 Sep 2025 00:27:05 +0200
|
||||
Subject: [PATCH] lib: Exclude the content model from allocation tracking
|
||||
|
||||
.. so that applications that are not using XML_FreeContentModel
|
||||
but plain free(..) or .free_fcn() to free the content model's
|
||||
memory are safe
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/7e35240dc97e9fd4f609e31f27c27b659535e436]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 16 +++++++++++++---
|
||||
1 file changed, 13 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 00139b94..d0b6e0cd 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -2785,7 +2785,10 @@ void XMLCALL
|
||||
XML_FreeContentModel(XML_Parser parser, XML_Content *model) {
|
||||
if (parser == NULL)
|
||||
return;
|
||||
- FREE(parser, model);
|
||||
+
|
||||
+ // NOTE: We are avoiding FREE(..) here because the content model
|
||||
+ // has been created using plain .malloc_fcn(..) rather than MALLOC(..).
|
||||
+ parser->m_mem.free_fcn(model);
|
||||
}
|
||||
|
||||
void *XMLCALL
|
||||
@@ -6063,8 +6066,12 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
|
||||
case XML_ROLE_CONTENT_EMPTY:
|
||||
if (dtd->in_eldecl) {
|
||||
if (parser->m_elementDeclHandler) {
|
||||
+ // NOTE: We are avoiding MALLOC(..) here to so that
|
||||
+ // applications that are not using XML_FreeContentModel but
|
||||
+ // plain free(..) or .free_fcn() to free the content model's
|
||||
+ // memory are safe.
|
||||
XML_Content *content
|
||||
- = (XML_Content *)MALLOC(parser, sizeof(XML_Content));
|
||||
+ = (XML_Content *)parser->m_mem.malloc_fcn(sizeof(XML_Content));
|
||||
if (! content)
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
content->quant = XML_CQUANT_NONE;
|
||||
@@ -8278,7 +8285,10 @@ build_model(XML_Parser parser) {
|
||||
const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content)
|
||||
+ (dtd->contentStringLen * sizeof(XML_Char)));
|
||||
|
||||
- ret = (XML_Content *)MALLOC(parser, allocsize);
|
||||
+ // NOTE: We are avoiding MALLOC(..) here to so that
|
||||
+ // applications that are not using XML_FreeContentModel but plain
|
||||
+ // free(..) or .free_fcn() to free the content model's memory are safe.
|
||||
+ ret = (XML_Content *)parser->m_mem.malloc_fcn(allocsize);
|
||||
if (! ret)
|
||||
return NULL;
|
||||
|
||||
309
meta/recipes-core/expat/expat/CVE-2025-59375-13.patch
Normal file
309
meta/recipes-core/expat/expat/CVE-2025-59375-13.patch
Normal file
@@ -0,0 +1,309 @@
|
||||
From 31f9053c3c46741f4daf2ea2bdea75f40f720d42 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Tue, 2 Sep 2025 22:36:49 +0200
|
||||
Subject: [PATCH] tests: Cover allocation tracking and limiting with tests
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/31f9053c3c46741f4daf2ea2bdea75f40f720d42]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/internal.h | 3 +
|
||||
lib/xmlparse.c | 12 +++
|
||||
tests/alloc_tests.c | 214 ++++++++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 229 insertions(+)
|
||||
|
||||
diff --git a/lib/internal.h b/lib/internal.h
|
||||
index eb67cf50..6e087858 100644
|
||||
--- a/lib/internal.h
|
||||
+++ b/lib/internal.h
|
||||
@@ -173,6 +173,9 @@ extern
|
||||
#endif
|
||||
XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c
|
||||
#if defined(XML_TESTING)
|
||||
+void *expat_malloc(XML_Parser parser, size_t size, int sourceLine);
|
||||
+void expat_free(XML_Parser parser, void *ptr, int sourceLine);
|
||||
+void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine);
|
||||
extern unsigned int g_bytesScanned; // used for testing only
|
||||
#endif
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index d0b6e0cd..6e9c6fb2 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -843,7 +843,11 @@ expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase,
|
||||
return tolerable;
|
||||
}
|
||||
|
||||
+# if defined(XML_TESTING)
|
||||
+void *
|
||||
+# else
|
||||
static void *
|
||||
+# endif
|
||||
expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
|
||||
// Detect integer overflow
|
||||
if (SIZE_MAX - size < sizeof(size_t)) {
|
||||
@@ -893,7 +897,11 @@ expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
|
||||
return (char *)mallocedPtr + sizeof(size_t);
|
||||
}
|
||||
|
||||
+# if defined(XML_TESTING)
|
||||
+void
|
||||
+# else
|
||||
static void
|
||||
+# endif
|
||||
expat_free(XML_Parser parser, void *ptr, int sourceLine) {
|
||||
assert(parser != NULL);
|
||||
|
||||
@@ -924,7 +932,11 @@ expat_free(XML_Parser parser, void *ptr, int sourceLine) {
|
||||
parser->m_mem.free_fcn(mallocedPtr);
|
||||
}
|
||||
|
||||
+# if defined(XML_TESTING)
|
||||
+void *
|
||||
+# else
|
||||
static void *
|
||||
+# endif
|
||||
expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
|
||||
assert(parser != NULL);
|
||||
|
||||
diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c
|
||||
index 4c3e2af4..275f92d5 100644
|
||||
--- a/tests/alloc_tests.c
|
||||
+++ b/tests/alloc_tests.c
|
||||
@@ -46,10 +46,16 @@
|
||||
# undef NDEBUG /* because test suite relies on assert(...) at the moment */
|
||||
#endif
|
||||
|
||||
+#include <math.h> /* NAN, INFINITY */
|
||||
+#include <stdbool.h>
|
||||
+#include <stdint.h> /* for SIZE_MAX */
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
+#include "expat_config.h"
|
||||
+
|
||||
#include "expat.h"
|
||||
+#include "internal.h"
|
||||
#include "common.h"
|
||||
#include "minicheck.h"
|
||||
#include "dummy.h"
|
||||
@@ -2085,6 +2091,203 @@ START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) {
|
||||
}
|
||||
END_TEST
|
||||
|
||||
+START_TEST(test_alloc_tracker_size_recorded) {
|
||||
+ XML_Memory_Handling_Suite memsuite = {malloc, realloc, free};
|
||||
+
|
||||
+ bool values[] = {true, false};
|
||||
+ for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) {
|
||||
+ const bool useMemSuite = values[i];
|
||||
+ set_subtest("useMemSuite=%d", (int)useMemSuite);
|
||||
+ XML_Parser parser = useMemSuite
|
||||
+ ? XML_ParserCreate_MM(NULL, &memsuite, XCS("|"))
|
||||
+ : XML_ParserCreate(NULL);
|
||||
+
|
||||
+#if XML_GE == 1
|
||||
+ void *ptr = expat_malloc(parser, 10, -1);
|
||||
+
|
||||
+ assert_true(ptr != NULL);
|
||||
+ assert_true(*((size_t *)ptr - 1) == 10);
|
||||
+
|
||||
+ assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL);
|
||||
+
|
||||
+ assert_true(*((size_t *)ptr - 1) == 10); // i.e. unchanged
|
||||
+
|
||||
+ ptr = expat_realloc(parser, ptr, 20, -1);
|
||||
+
|
||||
+ assert_true(ptr != NULL);
|
||||
+ assert_true(*((size_t *)ptr - 1) == 20);
|
||||
+
|
||||
+ expat_free(parser, ptr, -1);
|
||||
+#endif
|
||||
+
|
||||
+ XML_ParserFree(parser);
|
||||
+ }
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
+START_TEST(test_alloc_tracker_maximum_amplification) {
|
||||
+ if (g_reparseDeferralEnabledDefault == XML_TRUE) {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+
|
||||
+ // Get .m_accounting.countBytesDirect from 0 to 3
|
||||
+ const char *const chunk = "<e>";
|
||||
+ assert_true(_XML_Parse_SINGLE_BYTES(parser, chunk, (int)strlen(chunk),
|
||||
+ /*isFinal=*/XML_FALSE)
|
||||
+ == XML_STATUS_OK);
|
||||
+
|
||||
+#if XML_GE == 1
|
||||
+ // Stop activation threshold from interfering
|
||||
+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE);
|
||||
+
|
||||
+ // Exceed maximum amplification: should be rejected.
|
||||
+ assert_true(expat_malloc(parser, 1000, -1) == NULL);
|
||||
+
|
||||
+ // Increase maximum amplification, and try the same amount once more: should
|
||||
+ // work.
|
||||
+ assert_true(XML_SetAllocTrackerMaximumAmplification(parser, 3000.0f)
|
||||
+ == XML_TRUE);
|
||||
+
|
||||
+ void *const ptr = expat_malloc(parser, 1000, -1);
|
||||
+ assert_true(ptr != NULL);
|
||||
+ expat_free(parser, ptr, -1);
|
||||
+#endif
|
||||
+
|
||||
+ XML_ParserFree(parser);
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
+START_TEST(test_alloc_tracker_threshold) {
|
||||
+ XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+
|
||||
+#if XML_GE == 1
|
||||
+ // Exceed maximum amplification *before* (default) threshold: should work.
|
||||
+ void *const ptr = expat_malloc(parser, 1000, -1);
|
||||
+ assert_true(ptr != NULL);
|
||||
+ expat_free(parser, ptr, -1);
|
||||
+
|
||||
+ // Exceed maximum amplification *after* threshold: should be rejected.
|
||||
+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 999) == XML_TRUE);
|
||||
+ assert_true(expat_malloc(parser, 1000, -1) == NULL);
|
||||
+#endif
|
||||
+
|
||||
+ XML_ParserFree(parser);
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
+START_TEST(test_alloc_tracker_getbuffer_unlimited) {
|
||||
+ XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+
|
||||
+#if XML_GE == 1
|
||||
+ // Artificially lower threshold
|
||||
+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE);
|
||||
+
|
||||
+ // Self-test: Prove that threshold is as rejecting as expected
|
||||
+ assert_true(expat_malloc(parser, 1000, -1) == NULL);
|
||||
+#endif
|
||||
+ // XML_GetBuffer should be allowed to pass, though
|
||||
+ assert_true(XML_GetBuffer(parser, 1000) != NULL);
|
||||
+
|
||||
+ XML_ParserFree(parser);
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
+START_TEST(test_alloc_tracker_api) {
|
||||
+ XML_Parser parserWithoutParent = XML_ParserCreate(NULL);
|
||||
+ XML_Parser parserWithParent = XML_ExternalEntityParserCreate(
|
||||
+ parserWithoutParent, XCS("entity123"), NULL);
|
||||
+ if (parserWithoutParent == NULL)
|
||||
+ fail("parserWithoutParent is NULL");
|
||||
+ if (parserWithParent == NULL)
|
||||
+ fail("parserWithParent is NULL");
|
||||
+
|
||||
+#if XML_GE == 1
|
||||
+ // XML_SetAllocTrackerMaximumAmplification, error cases
|
||||
+ if (XML_SetAllocTrackerMaximumAmplification(NULL, 123.0f) == XML_TRUE)
|
||||
+ fail("Call with NULL parser is NOT supposed to succeed");
|
||||
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithParent, 123.0f)
|
||||
+ == XML_TRUE)
|
||||
+ fail("Call with non-root parser is NOT supposed to succeed");
|
||||
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, NAN)
|
||||
+ == XML_TRUE)
|
||||
+ fail("Call with NaN limit is NOT supposed to succeed");
|
||||
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, -1.0f)
|
||||
+ == XML_TRUE)
|
||||
+ fail("Call with negative limit is NOT supposed to succeed");
|
||||
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 0.9f)
|
||||
+ == XML_TRUE)
|
||||
+ fail("Call with positive limit <1.0 is NOT supposed to succeed");
|
||||
+
|
||||
+ // XML_SetAllocTrackerMaximumAmplification, success cases
|
||||
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 1.0f)
|
||||
+ == XML_FALSE)
|
||||
+ fail("Call with positive limit >=1.0 is supposed to succeed");
|
||||
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 123456.789f)
|
||||
+ == XML_FALSE)
|
||||
+ fail("Call with positive limit >=1.0 is supposed to succeed");
|
||||
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, INFINITY)
|
||||
+ == XML_FALSE)
|
||||
+ fail("Call with positive limit >=1.0 is supposed to succeed");
|
||||
+
|
||||
+ // XML_SetAllocTrackerActivationThreshold, error cases
|
||||
+ if (XML_SetAllocTrackerActivationThreshold(NULL, 123) == XML_TRUE)
|
||||
+ fail("Call with NULL parser is NOT supposed to succeed");
|
||||
+ if (XML_SetAllocTrackerActivationThreshold(parserWithParent, 123) == XML_TRUE)
|
||||
+ fail("Call with non-root parser is NOT supposed to succeed");
|
||||
+
|
||||
+ // XML_SetAllocTrackerActivationThreshold, success cases
|
||||
+ if (XML_SetAllocTrackerActivationThreshold(parserWithoutParent, 123)
|
||||
+ == XML_FALSE)
|
||||
+ fail("Call with non-NULL parentless parser is supposed to succeed");
|
||||
+#endif // XML_GE == 1
|
||||
+
|
||||
+ XML_ParserFree(parserWithParent);
|
||||
+ XML_ParserFree(parserWithoutParent);
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
+START_TEST(test_mem_api_cycle) {
|
||||
+ XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+
|
||||
+ void *ptr = XML_MemMalloc(parser, 10);
|
||||
+
|
||||
+ assert_true(ptr != NULL);
|
||||
+ memset(ptr, 'x', 10); // assert writability, with ASan in mind
|
||||
+
|
||||
+ ptr = XML_MemRealloc(parser, ptr, 20);
|
||||
+
|
||||
+ assert_true(ptr != NULL);
|
||||
+ memset(ptr, 'y', 20); // assert writability, with ASan in mind
|
||||
+
|
||||
+ XML_MemFree(parser, ptr);
|
||||
+
|
||||
+ XML_ParserFree(parser);
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
+START_TEST(test_mem_api_unlimited) {
|
||||
+ XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+
|
||||
+#if XML_GE == 1
|
||||
+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE);
|
||||
+#endif
|
||||
+
|
||||
+ void *ptr = XML_MemMalloc(parser, 1000);
|
||||
+
|
||||
+ assert_true(ptr != NULL);
|
||||
+
|
||||
+ ptr = XML_MemRealloc(parser, ptr, 2000);
|
||||
+
|
||||
+ assert_true(ptr != NULL);
|
||||
+
|
||||
+ XML_MemFree(parser, ptr);
|
||||
+
|
||||
+ XML_ParserFree(parser);
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
void
|
||||
make_alloc_test_case(Suite *s) {
|
||||
TCase *tc_alloc = tcase_create("allocation tests");
|
||||
@@ -2151,4 +2354,15 @@ make_alloc_test_case(Suite *s) {
|
||||
|
||||
tcase_add_test__ifdef_xml_dtd(
|
||||
tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail);
|
||||
+
|
||||
+ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_size_recorded);
|
||||
+ tcase_add_test__ifdef_xml_dtd(tc_alloc,
|
||||
+ test_alloc_tracker_maximum_amplification);
|
||||
+ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_threshold);
|
||||
+ tcase_add_test__ifdef_xml_dtd(tc_alloc,
|
||||
+ test_alloc_tracker_getbuffer_unlimited);
|
||||
+ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_api);
|
||||
+
|
||||
+ tcase_add_test(tc_alloc, test_mem_api_cycle);
|
||||
+ tcase_add_test__ifdef_xml_dtd(tc_alloc, test_mem_api_unlimited);
|
||||
}
|
||||
122
meta/recipes-core/expat/expat/CVE-2025-59375-14.patch
Normal file
122
meta/recipes-core/expat/expat/CVE-2025-59375-14.patch
Normal file
@@ -0,0 +1,122 @@
|
||||
From 78366891a586f293aeff60a14a55e4afe1169586 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Tue, 2 Sep 2025 16:44:00 +0200
|
||||
Subject: [PATCH] xmlwf: Wire allocation tracker config to existing arguments
|
||||
-a and -b
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/78366891a586f293aeff60a14a55e4afe1169586]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
doc/xmlwf.xml | 26 ++++++++++++++++++++------
|
||||
xmlwf/xmlwf.c | 7 +++++--
|
||||
xmlwf/xmlwf_helpgen.py | 4 ++--
|
||||
3 files changed, 27 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/doc/xmlwf.xml b/doc/xmlwf.xml
|
||||
index 17e9cf51..65d8ae9b 100644
|
||||
--- a/doc/xmlwf.xml
|
||||
+++ b/doc/xmlwf.xml
|
||||
@@ -158,19 +158,31 @@ supports both.
|
||||
<listitem>
|
||||
<para>
|
||||
Sets the maximum tolerated amplification factor
|
||||
- for protection against billion laughs attacks (default: 100.0).
|
||||
+ for protection against amplification attacks
|
||||
+ like the billion laughs attack
|
||||
+ (default: 100.0
|
||||
+ for the sum of direct and indirect output and also
|
||||
+ for allocations of dynamic memory).
|
||||
The amplification factor is calculated as ..
|
||||
</para>
|
||||
<literallayout>
|
||||
amplification := (direct + indirect) / direct
|
||||
</literallayout>
|
||||
<para>
|
||||
- .. while parsing, whereas
|
||||
+ .. with regard to use of entities and ..
|
||||
+ </para>
|
||||
+ <literallayout>
|
||||
+ amplification := allocated / direct
|
||||
+ </literallayout>
|
||||
+ <para>
|
||||
+ .. with regard to dynamic memory while parsing.
|
||||
<direct> is the number of bytes read
|
||||
- from the primary document in parsing and
|
||||
+ from the primary document in parsing,
|
||||
<indirect> is the number of bytes
|
||||
added by expanding entities and reading of external DTD files,
|
||||
- combined.
|
||||
+ combined, and
|
||||
+ <allocated> is the total number of bytes of dynamic memory
|
||||
+ allocated (and not freed) per hierarchy of parsers.
|
||||
</para>
|
||||
<para>
|
||||
<emphasis>NOTE</emphasis>:
|
||||
@@ -185,8 +197,10 @@ supports both.
|
||||
<listitem>
|
||||
<para>
|
||||
Sets the number of output bytes (including amplification)
|
||||
- needed to activate protection against billion laughs attacks
|
||||
- (default: 8 MiB).
|
||||
+ needed to activate protection against amplification attacks
|
||||
+ like billion laughs
|
||||
+ (default: 8 MiB for the sum of direct and indirect output,
|
||||
+ and 64 MiB for allocations of dynamic memory).
|
||||
This can be thought of as an "activation threshold".
|
||||
</para>
|
||||
<para>
|
||||
diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c
|
||||
index b9d0a7fc..14206d9e 100644
|
||||
--- a/xmlwf/xmlwf.c
|
||||
+++ b/xmlwf/xmlwf.c
|
||||
@@ -913,11 +913,11 @@ usage(const XML_Char *prog, int rc) {
|
||||
T(" -t write no XML output for [t]iming of plain parsing\n")
|
||||
T(" -N enable adding doctype and [n]otation declarations\n")
|
||||
T("\n")
|
||||
- T("billion laughs attack protection:\n")
|
||||
+ T("amplification attack protection (e.g. billion laughs):\n")
|
||||
T(" NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n")
|
||||
T("\n")
|
||||
T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n")
|
||||
- T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n")
|
||||
+ T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)\n")
|
||||
T("\n")
|
||||
T("reparse deferral:\n")
|
||||
T(" -q disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n")
|
||||
@@ -1181,12 +1181,15 @@ tmain(int argc, XML_Char **argv) {
|
||||
#if XML_GE == 1
|
||||
XML_SetBillionLaughsAttackProtectionMaximumAmplification(
|
||||
parser, attackMaximumAmplification);
|
||||
+ XML_SetAllocTrackerMaximumAmplification(parser,
|
||||
+ attackMaximumAmplification);
|
||||
#endif
|
||||
}
|
||||
if (attackThresholdGiven) {
|
||||
#if XML_GE == 1
|
||||
XML_SetBillionLaughsAttackProtectionActivationThreshold(
|
||||
parser, attackThresholdBytes);
|
||||
+ XML_SetAllocTrackerActivationThreshold(parser, attackThresholdBytes);
|
||||
#else
|
||||
(void)attackThresholdBytes; // silence -Wunused-but-set-variable
|
||||
#endif
|
||||
diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py
|
||||
index 2360820d..e91c285c 100755
|
||||
--- a/xmlwf/xmlwf_helpgen.py
|
||||
+++ b/xmlwf/xmlwf_helpgen.py
|
||||
@@ -84,13 +84,13 @@ output_mode.add_argument('-m', action='store_true', help='write [m]eta XML, not
|
||||
output_mode.add_argument('-t', action='store_true', help='write no XML output for [t]iming of plain parsing')
|
||||
output_related.add_argument('-N', action='store_true', help='enable adding doctype and [n]otation declarations')
|
||||
|
||||
-billion_laughs = parser.add_argument_group('billion laughs attack protection',
|
||||
+billion_laughs = parser.add_argument_group('amplification attack protection (e.g. billion laughs)',
|
||||
description='NOTE: '
|
||||
'If you ever need to increase these values '
|
||||
'for non-attack payload, please file a bug report.')
|
||||
billion_laughs.add_argument('-a', metavar='FACTOR',
|
||||
help='set maximum tolerated [a]mplification factor (default: 100.0)')
|
||||
-billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)')
|
||||
+billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)')
|
||||
|
||||
reparse_deferral = parser.add_argument_group('reparse deferral')
|
||||
reparse_deferral.add_argument('-q', metavar='FACTOR',
|
||||
70
meta/recipes-core/expat/expat/CVE-2025-59375-15.patch
Normal file
70
meta/recipes-core/expat/expat/CVE-2025-59375-15.patch
Normal file
@@ -0,0 +1,70 @@
|
||||
From 5ae51be57ed0ca1e87582881d07ea9c29c4f7c05 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Wed, 3 Sep 2025 17:06:41 +0200
|
||||
Subject: [PATCH] fuzz: Be robust towards NULL return from
|
||||
XML_ExternalEntityParserCreate
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/5ae51be57ed0ca1e87582881d07ea9c29c4f7c05]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
fuzz/xml_parse_fuzzer.c | 14 ++++++++------
|
||||
fuzz/xml_parsebuffer_fuzzer.c | 14 ++++++++------
|
||||
2 files changed, 16 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/fuzz/xml_parse_fuzzer.c b/fuzz/xml_parse_fuzzer.c
|
||||
index 90c38549..29ab33ff 100644
|
||||
--- a/fuzz/xml_parse_fuzzer.c
|
||||
+++ b/fuzz/xml_parse_fuzzer.c
|
||||
@@ -89,15 +89,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
|
||||
XML_Parser externalEntityParser
|
||||
= XML_ExternalEntityParserCreate(parentParser, "e1", NULL);
|
||||
- assert(externalEntityParser);
|
||||
- ParseOneInput(externalEntityParser, data, size);
|
||||
- XML_ParserFree(externalEntityParser);
|
||||
+ if (externalEntityParser != NULL) {
|
||||
+ ParseOneInput(externalEntityParser, data, size);
|
||||
+ XML_ParserFree(externalEntityParser);
|
||||
+ }
|
||||
|
||||
XML_Parser externalDtdParser
|
||||
= XML_ExternalEntityParserCreate(parentParser, NULL, NULL);
|
||||
- assert(externalDtdParser);
|
||||
- ParseOneInput(externalDtdParser, data, size);
|
||||
- XML_ParserFree(externalDtdParser);
|
||||
+ if (externalDtdParser != NULL) {
|
||||
+ ParseOneInput(externalDtdParser, data, size);
|
||||
+ XML_ParserFree(externalDtdParser);
|
||||
+ }
|
||||
|
||||
// finally frees this parser which served as parent
|
||||
XML_ParserFree(parentParser);
|
||||
diff --git a/fuzz/xml_parsebuffer_fuzzer.c b/fuzz/xml_parsebuffer_fuzzer.c
|
||||
index 0db67dce..38b9981b 100644
|
||||
--- a/fuzz/xml_parsebuffer_fuzzer.c
|
||||
+++ b/fuzz/xml_parsebuffer_fuzzer.c
|
||||
@@ -101,15 +101,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
|
||||
XML_Parser externalEntityParser
|
||||
= XML_ExternalEntityParserCreate(parentParser, "e1", NULL);
|
||||
- assert(externalEntityParser);
|
||||
- ParseOneInput(externalEntityParser, data, size);
|
||||
- XML_ParserFree(externalEntityParser);
|
||||
+ if (externalEntityParser != NULL) {
|
||||
+ ParseOneInput(externalEntityParser, data, size);
|
||||
+ XML_ParserFree(externalEntityParser);
|
||||
+ }
|
||||
|
||||
XML_Parser externalDtdParser
|
||||
= XML_ExternalEntityParserCreate(parentParser, NULL, NULL);
|
||||
- assert(externalDtdParser);
|
||||
- ParseOneInput(externalDtdParser, data, size);
|
||||
- XML_ParserFree(externalDtdParser);
|
||||
+ if (externalDtdParser != NULL) {
|
||||
+ ParseOneInput(externalDtdParser, data, size);
|
||||
+ XML_ParserFree(externalDtdParser);
|
||||
+ }
|
||||
|
||||
// finally frees this parser which served as parent
|
||||
XML_ParserFree(parentParser);
|
||||
146
meta/recipes-core/expat/expat/CVE-2025-59375-16.patch
Normal file
146
meta/recipes-core/expat/expat/CVE-2025-59375-16.patch
Normal file
@@ -0,0 +1,146 @@
|
||||
From d6246c31a1238d065b4d9690d3bac740326f6485 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Wed, 3 Sep 2025 01:28:03 +0200
|
||||
Subject: [PATCH] docs: Document the two allocation tracking API functions
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/d6246c31a1238d065b4d9690d3bac740326f6485]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
doc/reference.html | 116 +++++++++++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 116 insertions(+)
|
||||
|
||||
diff --git a/doc/reference.html b/doc/reference.html
|
||||
index 89476710..81da4e6c 100644
|
||||
--- a/doc/reference.html
|
||||
+++ b/doc/reference.html
|
||||
@@ -157,6 +157,8 @@ interface.</p>
|
||||
<ul>
|
||||
<li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li>
|
||||
<li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li>
|
||||
+ <li><a href="#XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</a></li>
|
||||
+ <li><a href="#XML_SetAllocTrackerActivationThreshold">XML_SetAllocTrackerActivationThreshold</a></li>
|
||||
<li><a href="#XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@@ -2262,6 +2264,120 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
|
||||
</p>
|
||||
</div>
|
||||
|
||||
+<h4 id="XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</h4>
|
||||
+<pre class="fcndec">
|
||||
+/* Added in Expat 2.7.2. */
|
||||
+XML_Bool
|
||||
+XML_SetAllocTrackerMaximumAmplification(XML_Parser p,
|
||||
+ float maximumAmplificationFactor);
|
||||
+</pre>
|
||||
+<div class="fcndef">
|
||||
+ <p>
|
||||
+ Sets the maximum tolerated amplification factor
|
||||
+ between direct input and bytes of dynamic memory allocated
|
||||
+ (default: <code>100.0</code>)
|
||||
+ of parser <code>p</code> to <code>maximumAmplificationFactor</code>, and
|
||||
+ returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error.
|
||||
+ </p>
|
||||
+
|
||||
+ <p>
|
||||
+ <strong>Note:</strong>
|
||||
+ There are three types of allocations that intentionally bypass tracking and limiting:
|
||||
+ </p>
|
||||
+ <ul>
|
||||
+ <li>
|
||||
+ application calls to functions
|
||||
+ <code><a href="#XML_MemMalloc">XML_MemMalloc</a></code>
|
||||
+ and
|
||||
+ <code><a href="#XML_MemRealloc">XML_MemRealloc</a></code>
|
||||
+ —
|
||||
+ <em>healthy</em> use of these two functions continues to be a responsibility
|
||||
+ of the application using Expat
|
||||
+ —,
|
||||
+ </li>
|
||||
+ <li>
|
||||
+ the main character buffer used by functions
|
||||
+ <code><a href="#XML_GetBuffer">XML_GetBuffer</a></code>
|
||||
+ and
|
||||
+ <code><a href="#XML_ParseBuffer">XML_ParseBuffer</a></code>
|
||||
+ (and thus also by plain
|
||||
+ <code><a href="#XML_Parse">XML_Parse</a></code>), and
|
||||
+ </li>
|
||||
+ <li>
|
||||
+ the <a href="#XML_SetElementDeclHandler">content model memory</a>
|
||||
+ (that is passed to the
|
||||
+ <a href="#XML_SetElementDeclHandler">element declaration handler</a>
|
||||
+ and freed by a call to
|
||||
+ <code><a href="#XML_FreeContentModel">XML_FreeContentModel</a></code>).
|
||||
+ </li>
|
||||
+ </ul>
|
||||
+
|
||||
+ <p>The amplification factor is calculated as ..</p>
|
||||
+ <pre>amplification := allocated / direct</pre>
|
||||
+ <p>
|
||||
+ .. while parsing, whereas
|
||||
+ <code>direct</code> is the number of bytes read from the primary document in parsing and
|
||||
+ <code>allocated</code> is the number of bytes of dynamic memory allocated in the parser hierarchy.
|
||||
+ </p>
|
||||
+
|
||||
+ <p>For a call to <code>XML_SetAllocTrackerMaximumAmplification</code> to succeed:</p>
|
||||
+ <ul>
|
||||
+ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers) and</li>
|
||||
+ <li><code>maximumAmplificationFactor</code> must be non-<code>NaN</code> and greater than or equal to <code>1.0</code>.</li>
|
||||
+ </ul>
|
||||
+
|
||||
+ <p>
|
||||
+ <strong>Note:</strong>
|
||||
+ If you ever need to increase this value for non-attack payload,
|
||||
+ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
|
||||
+ </p>
|
||||
+
|
||||
+ <p>
|
||||
+ <strong>Note:</strong>
|
||||
+ Amplifications factors greater than 100 can been observed near the start of parsing
|
||||
+ even with benign files in practice.
|
||||
+
|
||||
+ So if you do reduce the maximum allowed amplification,
|
||||
+ please make sure that the activation threshold is still big enough
|
||||
+ to not end up with undesired false positives (i.e. benign files being rejected).
|
||||
+ </p>
|
||||
+</div>
|
||||
+
|
||||
+<h4 id="XML_SetAllocTrackerActivationThreshold">XML_SetAllocTrackerActivationThreshold</h4>
|
||||
+<pre class="fcndec">
|
||||
+/* Added in Expat 2.7.2. */
|
||||
+XML_Bool
|
||||
+XML_SetAllocTrackerActivationThreshold(XML_Parser p,
|
||||
+ unsigned long long activationThresholdBytes);
|
||||
+</pre>
|
||||
+<div class="fcndef">
|
||||
+ <p>
|
||||
+ Sets number of allocated bytes of dynamic memory
|
||||
+ needed to activate protection against disproportionate use of RAM
|
||||
+ (default: <code>64 MiB</code>)
|
||||
+ of parser <code>p</code> to <code>activationThresholdBytes</code>, and
|
||||
+ returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error.
|
||||
+ </p>
|
||||
+
|
||||
+ <p>
|
||||
+ <strong>Note:</strong>
|
||||
+ For types of allocations that intentionally bypass tracking and limiting, please see
|
||||
+ <code><a href="#XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</a></code>
|
||||
+ above.
|
||||
+ </p>
|
||||
+
|
||||
+ <p>For a call to <code>XML_SetAllocTrackerActivationThreshold</code> to succeed:</p>
|
||||
+ <ul>
|
||||
+ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers).</li>
|
||||
+ </ul>
|
||||
+
|
||||
+ <p>
|
||||
+ <strong>Note:</strong>
|
||||
+ If you ever need to increase this value for non-attack payload,
|
||||
+ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
|
||||
+ </p>
|
||||
+</div>
|
||||
+
|
||||
<h4 id="XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</h4>
|
||||
<pre class="fcndec">
|
||||
/* Added in Expat 2.6.0. */
|
||||
28
meta/recipes-core/expat/expat/CVE-2025-59375-17.patch
Normal file
28
meta/recipes-core/expat/expat/CVE-2025-59375-17.patch
Normal file
@@ -0,0 +1,28 @@
|
||||
From a6a2a49367f03f5d8a73c9027b45b59953ca27d8 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Wed, 10 Sep 2025 19:52:39 +0200
|
||||
Subject: [PATCH] docs: Promote the contract to call XML_FreeContentModel
|
||||
|
||||
.. when registering a custom element declaration handler
|
||||
(via a call to function XML_SetElementDeclHandler)
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/a6a2a49367f03f5d8a73c9027b45b59953ca27d8]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
doc/reference.html | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/doc/reference.html b/doc/reference.html
|
||||
index 81da4e6c..564fc1b2 100644
|
||||
--- a/doc/reference.html
|
||||
+++ b/doc/reference.html
|
||||
@@ -1902,7 +1902,7 @@ struct XML_cp {
|
||||
<p>Sets a handler for element declarations in a DTD. The handler gets
|
||||
called with the name of the element in the declaration and a pointer
|
||||
to a structure that contains the element model. It's the user code's
|
||||
-responsibility to free model when finished with it. See <code>
|
||||
+responsibility to free model when finished with via a call to <code>
|
||||
<a href="#XML_FreeContentModel">XML_FreeContentModel</a></code>.
|
||||
There is no need to free the model from the handler, it can be kept
|
||||
around and freed at a later stage.</p>
|
||||
74
meta/recipes-core/expat/expat/CVE-2025-59375-18.patch
Normal file
74
meta/recipes-core/expat/expat/CVE-2025-59375-18.patch
Normal file
@@ -0,0 +1,74 @@
|
||||
From a21a3a8299e1ee0b0ae5ae2886a0746d088cf135 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Sun, 7 Sep 2025 16:00:35 +0200
|
||||
Subject: [PATCH] Changes: Document allocation tracking
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/a21a3a8299e1ee0b0ae5ae2886a0746d088cf135]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
Changes | 37 +++++++++++++++++++++++++++++++++++++
|
||||
1 file changed, 37 insertions(+)
|
||||
|
||||
diff --git a/Changes b/Changes
|
||||
index cb752151..ceb5c5dc 100644
|
||||
--- a/Changes
|
||||
+++ b/Changes
|
||||
@@ -30,6 +30,36 @@
|
||||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
Patches:
|
||||
+ Security fixes:
|
||||
+ #1018 #1034 CVE-2025-59375 -- Disallow use of disproportional amounts of
|
||||
+ dynamic memory from within an Expat parser (e.g. previously
|
||||
+ a ~250 KiB sized document was able to cause allocation of
|
||||
+ ~800 MiB from the heap, i.e. an "amplification" of factor
|
||||
+ ~3,300); once a threshold (that defaults to 64 MiB) is
|
||||
+ reached, a maximum amplification factor (that defaults to
|
||||
+ 100.0) is enforced, and violating documents are rejected
|
||||
+ with an out-of-memory error.
|
||||
+ There are two new API functions to fine-tune this new
|
||||
+ behavior:
|
||||
+ - XML_SetAllocTrackerActivationThreshold
|
||||
+ - XML_SetAllocTrackerMaximumAmplification .
|
||||
+ If you ever need to increase these defaults for non-attack
|
||||
+ XML payload, please file a bug report with libexpat.
|
||||
+ There is also a new environment variable
|
||||
+ EXPAT_MALLOC_DEBUG=(0|1|2) to control the verbosity
|
||||
+ of allocations debugging at runtime, disabled by default.
|
||||
+ Known impact is (reliable and easy) denial of service:
|
||||
+ CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/E:H/RL:O/RC:C
|
||||
+ (Base Score: 7.5, Temporal Score: 7.2)
|
||||
+ Please note that a layer of compression around XML can
|
||||
+ significantly reduce the minimum attack payload size.
|
||||
+ Distributors intending to backport (or cherry-pick) the
|
||||
+ fix need to copy 99% of the related pull request, not just
|
||||
+ the "lib: Implement tracking of dynamic memory allocations"
|
||||
+ commit, to not end up with a state that literally does both
|
||||
+ too much and too little at the same time. Appending ".diff"
|
||||
+ to the pull request URL could be of help.
|
||||
+
|
||||
Bug fixes:
|
||||
#980 #989 Restore event pointer behavior from Expat 2.6.4
|
||||
(that the fix to CVE-2024-8176 changed in 2.7.0);
|
||||
@@ -39,6 +69,10 @@ Patches:
|
||||
- XML_GetCurrentColumnNumber
|
||||
- XML_GetCurrentLineNumber
|
||||
- XML_GetInputContext
|
||||
+ #1034 docs: Promote the contract to call function
|
||||
+ XML_FreeContentModel when registering a custom
|
||||
+ element declaration handler (via a call to function
|
||||
+ XML_SetElementDeclHandler)
|
||||
|
||||
Special thanks to:
|
||||
Berkay Eren Ürün
|
||||
@@ -71,6 +105,9 @@ Patches:
|
||||
Linutronix
|
||||
Red Hat
|
||||
Siemens
|
||||
+ and
|
||||
+ OSS-Fuzz / ClusterFuzz
|
||||
+ Perl XML::Parser
|
||||
|
||||
Release 2.6.4 Wed November 6 2024
|
||||
Security fixes:
|
||||
103
meta/recipes-core/expat/expat/CVE-2025-59375-19.patch
Normal file
103
meta/recipes-core/expat/expat/CVE-2025-59375-19.patch
Normal file
@@ -0,0 +1,103 @@
|
||||
From f4b5bb033dc4430bbd31dcae8a55f988360bcec5 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Wed, 17 Sep 2025 23:14:02 +0200
|
||||
Subject: [PATCH] lib: Document and regression-proof absence of integer
|
||||
overflow from expat_realloc
|
||||
|
||||
Matthew Fernandez (@Smattr) and I teamed up on whether function expat_realloc
|
||||
could be vulnerable to integer overflow in line:
|
||||
|
||||
mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + size);
|
||||
^
|
||||
We ended up with a mathematical proof that, fortunately, the current code
|
||||
already is safe from overflow.
|
||||
|
||||
The proof uses technique "proof by contradiction". Let's assume, there *was* a
|
||||
risk of integer overflow. For a risk of overflow, these four conditions would
|
||||
all need to be met, together:
|
||||
|
||||
(1) `SIZE_MAX < sizeof(size_t) + size`
|
||||
or we would not hit an overflow on `size_t`.
|
||||
|
||||
(2) `size > prevSize`
|
||||
or `expat_malloc` would have already not allocated earlier
|
||||
as `expat_realloc` relies on `expat_malloc` for the initial allocation.
|
||||
|
||||
(3) `rootParser->m_alloc_tracker.bytesAllocated >= sizeof(size_t) + prevSize`
|
||||
or the previous allocation would be gone already or have bypassed accounting.
|
||||
The code is not thread-safe in general, race conditions are off the table.
|
||||
|
||||
(4) `rootParser->m_alloc_tracker.bytesAllocated + (size - prevSize) <= SIZE_MAX`
|
||||
or `expat_heap_increase_tolerable` would have returned `false` and
|
||||
the overflow line would not be reached.
|
||||
|
||||
We encoded this for the Z3 Theorem Prover (https://github.com/Z3Prover/z3)
|
||||
and ended up with this document:
|
||||
|
||||
$ cat proof_v2.smt2
|
||||
; Copyright (c) 2025 Matthew Fernandez <matthew.fernandez@gmail.com>
|
||||
; Copyright (c) 2025 Sebastian Pipping <sebastian@pipping.org>
|
||||
; Licensed under the MIT license
|
||||
|
||||
; (1), (2), (3), (4) form a contradiction
|
||||
|
||||
; define `SIZE_MAX`
|
||||
(declare-fun SIZE_MAX () (_ BitVec 64))
|
||||
(assert (= SIZE_MAX #xffffffffffffffff))
|
||||
|
||||
; define `sizeof(size_t)`
|
||||
(declare-fun sizeof_size_t () (_ BitVec 64))
|
||||
(assert (= sizeof_size_t #x0000000000000008))
|
||||
|
||||
; claim we have inputs `size`, `prevSize`, and `bytesAllocated`
|
||||
(declare-fun size () (_ BitVec 64))
|
||||
(declare-fun prevSize () (_ BitVec 64))
|
||||
(declare-fun bytesAllocated () (_ BitVec 64))
|
||||
|
||||
; assume `SIZE_MAX - sizeof(size_t) < size` (1)
|
||||
(assert (bvult (bvsub SIZE_MAX sizeof_size_t) size))
|
||||
|
||||
; assume `bytesAllocated >= sizeof(size_t) + prevSize` (3)
|
||||
(assert (bvuge bytesAllocated (bvadd sizeof_size_t prevSize)))
|
||||
|
||||
; assume `bytesAllocated - prevSize <= SIZE_MAX - size` (4)
|
||||
(assert (bvule (bvsub bytesAllocated prevSize) (bvsub SIZE_MAX size)))
|
||||
|
||||
; assume `SIZE_MAX - sizeof(size_t) >= prevSize` (anti-overflow for 3)
|
||||
(assert (bvuge (bvsub SIZE_MAX sizeof_size_t) prevSize))
|
||||
|
||||
; prove we have a contradiction
|
||||
(check-sat)
|
||||
|
||||
Note that we operate on fixed-size bit vectors here, and hence had
|
||||
to transform the assertions to not allow integer overflow by themselves.
|
||||
|
||||
Z3 confirms the contradiction, and thus the absence of integer overflow:
|
||||
|
||||
$ z3 -smt2 -model proof_v2.smt2
|
||||
unsat
|
||||
|
||||
Co-authored-by: Matthew Fernandez <matthew.fernandez@gmail.com>
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/f4b5bb033dc4430bbd31dcae8a55f988360bcec5]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index de159493..24fd7b97 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -969,6 +969,10 @@ expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
|
||||
}
|
||||
}
|
||||
|
||||
+ // NOTE: Integer overflow detection has already been done for us
|
||||
+ // by expat_heap_increase_tolerable(..) above
|
||||
+ assert(SIZE_MAX - sizeof(size_t) >= size);
|
||||
+
|
||||
// Actually allocate
|
||||
mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + size);
|
||||
|
||||
285
meta/recipes-core/expat/expat/CVE-2025-59375-20.patch
Normal file
285
meta/recipes-core/expat/expat/CVE-2025-59375-20.patch
Normal file
@@ -0,0 +1,285 @@
|
||||
From faf36f806c9065bfd9f0567b01924d5e27c4911c Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Mon, 15 Sep 2025 18:05:23 +0200
|
||||
Subject: [PATCH] lib: Drop casts around malloc/realloc returns that C99 does
|
||||
not need
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/faf36f806c9065bfd9f0567b01924d5e27c4911c]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/xmlparse.c | 80 ++++++++++++++++++++++----------------------------
|
||||
1 file changed, 35 insertions(+), 45 deletions(-)
|
||||
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 6e9c6fb2..fb8ad2e7 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -1370,12 +1370,12 @@ parserCreate(const XML_Char *encodingName,
|
||||
XML_Memory_Handling_Suite *mtemp;
|
||||
#if XML_GE == 1
|
||||
void *const sizeAndParser
|
||||
- = (XML_Parser)malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct));
|
||||
+ = malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct));
|
||||
if (sizeAndParser != NULL) {
|
||||
*(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
|
||||
parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t));
|
||||
#else
|
||||
- parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct));
|
||||
+ parser = malloc(sizeof(struct XML_ParserStruct));
|
||||
if (parser != NULL) {
|
||||
#endif
|
||||
mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
|
||||
@@ -1433,23 +1433,20 @@ parserCreate(const XML_Char *encodingName,
|
||||
parser->m_bufferLim = NULL;
|
||||
|
||||
parser->m_attsSize = INIT_ATTS_SIZE;
|
||||
- parser->m_atts
|
||||
- = (ATTRIBUTE *)MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE));
|
||||
+ parser->m_atts = MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE));
|
||||
if (parser->m_atts == NULL) {
|
||||
FREE(parser, parser);
|
||||
return NULL;
|
||||
}
|
||||
#ifdef XML_ATTR_INFO
|
||||
- parser->m_attInfo = (XML_AttrInfo *)MALLOC(
|
||||
- parser, parser->m_attsSize * sizeof(XML_AttrInfo));
|
||||
+ parser->m_attInfo = MALLOC(parser, parser->m_attsSize * sizeof(XML_AttrInfo));
|
||||
if (parser->m_attInfo == NULL) {
|
||||
FREE(parser, parser->m_atts);
|
||||
FREE(parser, parser);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
- parser->m_dataBuf
|
||||
- = (XML_Char *)MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char));
|
||||
+ parser->m_dataBuf = MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char));
|
||||
if (parser->m_dataBuf == NULL) {
|
||||
FREE(parser, parser->m_atts);
|
||||
#ifdef XML_ATTR_INFO
|
||||
@@ -2588,7 +2585,7 @@ XML_GetBuffer(XML_Parser parser, int len) {
|
||||
}
|
||||
// NOTE: We are avoiding MALLOC(..) here to leave limiting
|
||||
// the input size to the application using Expat.
|
||||
- newBuf = (char *)parser->m_mem.malloc_fcn(bufferSize);
|
||||
+ newBuf = parser->m_mem.malloc_fcn(bufferSize);
|
||||
if (newBuf == 0) {
|
||||
parser->m_errorCode = XML_ERROR_NO_MEMORY;
|
||||
return NULL;
|
||||
@@ -3133,7 +3130,7 @@ storeRawNames(XML_Parser parser) {
|
||||
return XML_FALSE;
|
||||
bufSize = nameLen + (int)rawNameLen;
|
||||
if (bufSize > tag->bufEnd - tag->buf) {
|
||||
- char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
|
||||
+ char *temp = REALLOC(parser, tag->buf, bufSize);
|
||||
if (temp == NULL)
|
||||
return XML_FALSE;
|
||||
/* if tag->name.str points to tag->buf (only when namespace
|
||||
@@ -3459,10 +3456,10 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
|
||||
tag = parser->m_freeTagList;
|
||||
parser->m_freeTagList = parser->m_freeTagList->parent;
|
||||
} else {
|
||||
- tag = (TAG *)MALLOC(parser, sizeof(TAG));
|
||||
+ tag = MALLOC(parser, sizeof(TAG));
|
||||
if (! tag)
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
- tag->buf = (char *)MALLOC(parser, INIT_TAG_BUF_SIZE);
|
||||
+ tag->buf = MALLOC(parser, INIT_TAG_BUF_SIZE);
|
||||
if (! tag->buf) {
|
||||
FREE(parser, tag);
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
@@ -3495,7 +3492,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
|
||||
}
|
||||
bufSize = (int)(tag->bufEnd - tag->buf) << 1;
|
||||
{
|
||||
- char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
|
||||
+ char *temp = REALLOC(parser, tag->buf, bufSize);
|
||||
if (temp == NULL)
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
tag->buf = temp;
|
||||
@@ -3874,8 +3871,8 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
|
||||
}
|
||||
#endif
|
||||
|
||||
- temp = (ATTRIBUTE *)REALLOC(parser, (void *)parser->m_atts,
|
||||
- parser->m_attsSize * sizeof(ATTRIBUTE));
|
||||
+ temp = REALLOC(parser, (void *)parser->m_atts,
|
||||
+ parser->m_attsSize * sizeof(ATTRIBUTE));
|
||||
if (temp == NULL) {
|
||||
parser->m_attsSize = oldAttsSize;
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
@@ -3893,8 +3890,8 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
|
||||
}
|
||||
# endif
|
||||
|
||||
- temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)parser->m_attInfo,
|
||||
- parser->m_attsSize * sizeof(XML_AttrInfo));
|
||||
+ temp2 = REALLOC(parser, (void *)parser->m_attInfo,
|
||||
+ parser->m_attsSize * sizeof(XML_AttrInfo));
|
||||
if (temp2 == NULL) {
|
||||
parser->m_attsSize = oldAttsSize;
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
@@ -4070,8 +4067,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
|
||||
}
|
||||
#endif
|
||||
|
||||
- temp = (NS_ATT *)REALLOC(parser, parser->m_nsAtts,
|
||||
- nsAttsSize * sizeof(NS_ATT));
|
||||
+ temp = REALLOC(parser, parser->m_nsAtts, nsAttsSize * sizeof(NS_ATT));
|
||||
if (! temp) {
|
||||
/* Restore actual size of memory in m_nsAtts */
|
||||
parser->m_nsAttsPower = oldNsAttsPower;
|
||||
@@ -4252,7 +4248,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
|
||||
}
|
||||
#endif
|
||||
|
||||
- uri = (XML_Char *)MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
|
||||
+ uri = MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
|
||||
if (! uri)
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
binding->uriAlloc = n + EXPAND_SPARE;
|
||||
@@ -4498,8 +4494,8 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
|
||||
}
|
||||
#endif
|
||||
|
||||
- XML_Char *temp = (XML_Char *)REALLOC(
|
||||
- parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
|
||||
+ XML_Char *temp
|
||||
+ = REALLOC(parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
|
||||
if (temp == NULL)
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
b->uri = temp;
|
||||
@@ -4507,7 +4503,7 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
|
||||
}
|
||||
parser->m_freeBindingList = b->nextTagBinding;
|
||||
} else {
|
||||
- b = (BINDING *)MALLOC(parser, sizeof(BINDING));
|
||||
+ b = MALLOC(parser, sizeof(BINDING));
|
||||
if (! b)
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
|
||||
@@ -4525,8 +4521,7 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
|
||||
}
|
||||
#endif
|
||||
|
||||
- b->uri
|
||||
- = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
|
||||
+ b->uri = MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
|
||||
if (! b->uri) {
|
||||
FREE(parser, b);
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
@@ -5897,7 +5892,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
}
|
||||
|
||||
- char *const new_connector = (char *)REALLOC(
|
||||
+ char *const new_connector = REALLOC(
|
||||
parser, parser->m_groupConnector, parser->m_groupSize *= 2);
|
||||
if (new_connector == NULL) {
|
||||
parser->m_groupSize /= 2;
|
||||
@@ -5917,15 +5912,14 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
|
||||
}
|
||||
#endif
|
||||
|
||||
- int *const new_scaff_index = (int *)REALLOC(
|
||||
+ int *const new_scaff_index = REALLOC(
|
||||
parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int));
|
||||
if (new_scaff_index == NULL)
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
dtd->scaffIndex = new_scaff_index;
|
||||
}
|
||||
} else {
|
||||
- parser->m_groupConnector
|
||||
- = (char *)MALLOC(parser, parser->m_groupSize = 32);
|
||||
+ parser->m_groupConnector = MALLOC(parser, parser->m_groupSize = 32);
|
||||
if (! parser->m_groupConnector) {
|
||||
parser->m_groupSize = 0;
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
@@ -6086,8 +6080,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
|
||||
// applications that are not using XML_FreeContentModel but
|
||||
// plain free(..) or .free_fcn() to free the content model's
|
||||
// memory are safe.
|
||||
- XML_Content *content
|
||||
- = (XML_Content *)parser->m_mem.malloc_fcn(sizeof(XML_Content));
|
||||
+ XML_Content *content = parser->m_mem.malloc_fcn(sizeof(XML_Content));
|
||||
if (! content)
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
content->quant = XML_CQUANT_NONE;
|
||||
@@ -6364,8 +6357,7 @@ processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl,
|
||||
openEntity = *freeEntityList;
|
||||
*freeEntityList = openEntity->next;
|
||||
} else {
|
||||
- openEntity
|
||||
- = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY));
|
||||
+ openEntity = MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY));
|
||||
if (! openEntity)
|
||||
return XML_ERROR_NO_MEMORY;
|
||||
}
|
||||
@@ -7164,8 +7156,8 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
|
||||
if (type->nDefaultAtts == type->allocDefaultAtts) {
|
||||
if (type->allocDefaultAtts == 0) {
|
||||
type->allocDefaultAtts = 8;
|
||||
- type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(
|
||||
- parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
|
||||
+ type->defaultAtts
|
||||
+ = MALLOC(parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
|
||||
if (! type->defaultAtts) {
|
||||
type->allocDefaultAtts = 0;
|
||||
return 0;
|
||||
@@ -7190,8 +7182,8 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
|
||||
}
|
||||
#endif
|
||||
|
||||
- temp = (DEFAULT_ATTRIBUTE *)REALLOC(parser, type->defaultAtts,
|
||||
- (count * sizeof(DEFAULT_ATTRIBUTE)));
|
||||
+ temp = REALLOC(parser, type->defaultAtts,
|
||||
+ (count * sizeof(DEFAULT_ATTRIBUTE)));
|
||||
if (temp == NULL)
|
||||
return 0;
|
||||
type->allocDefaultAtts = count;
|
||||
@@ -8145,8 +8137,7 @@ poolGrow(STRING_POOL *pool) {
|
||||
if (bytesToAllocate == 0)
|
||||
return XML_FALSE;
|
||||
|
||||
- temp = (BLOCK *)REALLOC(pool->parser, pool->blocks,
|
||||
- (unsigned)bytesToAllocate);
|
||||
+ temp = REALLOC(pool->parser, pool->blocks, (unsigned)bytesToAllocate);
|
||||
if (temp == NULL)
|
||||
return XML_FALSE;
|
||||
pool->blocks = temp;
|
||||
@@ -8217,7 +8208,7 @@ nextScaffoldPart(XML_Parser parser) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
- dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int));
|
||||
+ dtd->scaffIndex = MALLOC(parser, parser->m_groupSize * sizeof(int));
|
||||
if (! dtd->scaffIndex)
|
||||
return -1;
|
||||
dtd->scaffIndex[0] = 0;
|
||||
@@ -8240,14 +8231,13 @@ nextScaffoldPart(XML_Parser parser) {
|
||||
}
|
||||
#endif
|
||||
|
||||
- temp = (CONTENT_SCAFFOLD *)REALLOC(
|
||||
- parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
|
||||
+ temp = REALLOC(parser, dtd->scaffold,
|
||||
+ dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
|
||||
if (temp == NULL)
|
||||
return -1;
|
||||
dtd->scaffSize *= 2;
|
||||
} else {
|
||||
- temp = (CONTENT_SCAFFOLD *)MALLOC(parser, INIT_SCAFFOLD_ELEMENTS
|
||||
- * sizeof(CONTENT_SCAFFOLD));
|
||||
+ temp = MALLOC(parser, INIT_SCAFFOLD_ELEMENTS * sizeof(CONTENT_SCAFFOLD));
|
||||
if (temp == NULL)
|
||||
return -1;
|
||||
dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS;
|
||||
@@ -8304,7 +8294,7 @@ build_model(XML_Parser parser) {
|
||||
// NOTE: We are avoiding MALLOC(..) here to so that
|
||||
// applications that are not using XML_FreeContentModel but plain
|
||||
// free(..) or .free_fcn() to free the content model's memory are safe.
|
||||
- ret = (XML_Content *)parser->m_mem.malloc_fcn(allocsize);
|
||||
+ ret = parser->m_mem.malloc_fcn(allocsize);
|
||||
if (! ret)
|
||||
return NULL;
|
||||
|
||||
196
meta/recipes-core/expat/expat/CVE-2025-59375-21.patch
Normal file
196
meta/recipes-core/expat/expat/CVE-2025-59375-21.patch
Normal file
@@ -0,0 +1,196 @@
|
||||
From 4b43b8dacc96fd538254e17a69abc9745c3a2ed4 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Fri, 19 Sep 2025 23:32:46 +0200
|
||||
Subject: [PATCH] lib: Fix alignment of internal allocations for some non-amd64
|
||||
architectures
|
||||
|
||||
sparc32 is known to be affected.
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/4b43b8dacc96fd538254e17a69abc9745c3a2ed4]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
lib/internal.h | 6 ++++++
|
||||
lib/xmlparse.c | 38 ++++++++++++++++++++++----------------
|
||||
tests/alloc_tests.c | 13 ++++++++++---
|
||||
3 files changed, 38 insertions(+), 19 deletions(-)
|
||||
|
||||
diff --git a/lib/internal.h b/lib/internal.h
|
||||
index 6e087858..8f5edf48 100644
|
||||
--- a/lib/internal.h
|
||||
+++ b/lib/internal.h
|
||||
@@ -108,6 +108,7 @@
|
||||
#endif
|
||||
|
||||
#include <limits.h> // ULONG_MAX
|
||||
+#include <stddef.h> // size_t
|
||||
|
||||
#if defined(_WIN32) \
|
||||
&& (! defined(__USE_MINGW_ANSI_STDIO) \
|
||||
@@ -150,6 +151,11 @@
|
||||
#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \
|
||||
67108864 // 64 MiB, 2^26
|
||||
|
||||
+// NOTE: If function expat_alloc was user facing, EXPAT_MALLOC_ALIGNMENT would
|
||||
+// have to take sizeof(long double) into account
|
||||
+#define EXPAT_MALLOC_ALIGNMENT sizeof(long long) // largest parser (sub)member
|
||||
+#define EXPAT_MALLOC_PADDING ((EXPAT_MALLOC_ALIGNMENT) - sizeof(size_t))
|
||||
+
|
||||
/* NOTE END */
|
||||
|
||||
#include "expat.h" // so we can use type XML_Parser below
|
||||
diff --git a/lib/xmlparse.c b/lib/xmlparse.c
|
||||
index 24fd7b97..ce29ab6f 100644
|
||||
--- a/lib/xmlparse.c
|
||||
+++ b/lib/xmlparse.c
|
||||
@@ -850,14 +850,14 @@ static void *
|
||||
# endif
|
||||
expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
|
||||
// Detect integer overflow
|
||||
- if (SIZE_MAX - size < sizeof(size_t)) {
|
||||
+ if (SIZE_MAX - size < sizeof(size_t) + EXPAT_MALLOC_PADDING) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const XML_Parser rootParser = getRootParserOf(parser, NULL);
|
||||
assert(rootParser->m_parentParser == NULL);
|
||||
|
||||
- const size_t bytesToAllocate = sizeof(size_t) + size;
|
||||
+ const size_t bytesToAllocate = sizeof(size_t) + EXPAT_MALLOC_PADDING + size;
|
||||
|
||||
if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated
|
||||
< bytesToAllocate) {
|
||||
@@ -894,7 +894,7 @@ expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
|
||||
rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
|
||||
}
|
||||
|
||||
- return (char *)mallocedPtr + sizeof(size_t);
|
||||
+ return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING;
|
||||
}
|
||||
|
||||
# if defined(XML_TESTING)
|
||||
@@ -914,8 +914,9 @@ expat_free(XML_Parser parser, void *ptr, int sourceLine) {
|
||||
|
||||
// Extract size (to the eyes of malloc_fcn/realloc_fcn) and
|
||||
// the original pointer returned by malloc/realloc
|
||||
- void *const mallocedPtr = (char *)ptr - sizeof(size_t);
|
||||
- const size_t bytesAllocated = sizeof(size_t) + *(size_t *)mallocedPtr;
|
||||
+ void *const mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t);
|
||||
+ const size_t bytesAllocated
|
||||
+ = sizeof(size_t) + EXPAT_MALLOC_PADDING + *(size_t *)mallocedPtr;
|
||||
|
||||
// Update accounting
|
||||
assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated);
|
||||
@@ -954,7 +955,7 @@ expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
|
||||
|
||||
// Extract original size (to the eyes of the caller) and the original
|
||||
// pointer returned by malloc/realloc
|
||||
- void *mallocedPtr = (char *)ptr - sizeof(size_t);
|
||||
+ void *mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t);
|
||||
const size_t prevSize = *(size_t *)mallocedPtr;
|
||||
|
||||
// Classify upcoming change
|
||||
@@ -971,10 +972,11 @@ expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
|
||||
|
||||
// NOTE: Integer overflow detection has already been done for us
|
||||
// by expat_heap_increase_tolerable(..) above
|
||||
- assert(SIZE_MAX - sizeof(size_t) >= size);
|
||||
+ assert(SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING >= size);
|
||||
|
||||
// Actually allocate
|
||||
- mallocedPtr = parser->m_mem.realloc_fcn(mallocedPtr, sizeof(size_t) + size);
|
||||
+ mallocedPtr = parser->m_mem.realloc_fcn(
|
||||
+ mallocedPtr, sizeof(size_t) + EXPAT_MALLOC_PADDING + size);
|
||||
|
||||
if (mallocedPtr == NULL) {
|
||||
return NULL;
|
||||
@@ -1005,7 +1007,7 @@ expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
|
||||
// Update in-block recorded size
|
||||
*(size_t *)mallocedPtr = size;
|
||||
|
||||
- return (char *)mallocedPtr + sizeof(size_t);
|
||||
+ return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING;
|
||||
}
|
||||
#endif // XML_GE == 1
|
||||
|
||||
@@ -1337,7 +1339,8 @@ parserCreate(const XML_Char *encodingName,
|
||||
XML_Parser parser = NULL;
|
||||
|
||||
#if XML_GE == 1
|
||||
- const size_t increase = sizeof(size_t) + sizeof(struct XML_ParserStruct);
|
||||
+ const size_t increase
|
||||
+ = sizeof(size_t) + EXPAT_MALLOC_PADDING + sizeof(struct XML_ParserStruct);
|
||||
|
||||
if (parentParser != NULL) {
|
||||
const XML_Parser rootParser = getRootParserOf(parentParser, NULL);
|
||||
@@ -1352,11 +1355,13 @@ parserCreate(const XML_Char *encodingName,
|
||||
if (memsuite) {
|
||||
XML_Memory_Handling_Suite *mtemp;
|
||||
#if XML_GE == 1
|
||||
- void *const sizeAndParser = memsuite->malloc_fcn(
|
||||
- sizeof(size_t) + sizeof(struct XML_ParserStruct));
|
||||
+ void *const sizeAndParser
|
||||
+ = memsuite->malloc_fcn(sizeof(size_t) + EXPAT_MALLOC_PADDING
|
||||
+ + sizeof(struct XML_ParserStruct));
|
||||
if (sizeAndParser != NULL) {
|
||||
*(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
|
||||
- parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t));
|
||||
+ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)
|
||||
+ + EXPAT_MALLOC_PADDING);
|
||||
#else
|
||||
parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
|
||||
if (parser != NULL) {
|
||||
@@ -1369,11 +1374,12 @@ parserCreate(const XML_Char *encodingName,
|
||||
} else {
|
||||
XML_Memory_Handling_Suite *mtemp;
|
||||
#if XML_GE == 1
|
||||
- void *const sizeAndParser
|
||||
- = malloc(sizeof(size_t) + sizeof(struct XML_ParserStruct));
|
||||
+ void *const sizeAndParser = malloc(sizeof(size_t) + EXPAT_MALLOC_PADDING
|
||||
+ + sizeof(struct XML_ParserStruct));
|
||||
if (sizeAndParser != NULL) {
|
||||
*(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
|
||||
- parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t));
|
||||
+ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)
|
||||
+ + EXPAT_MALLOC_PADDING);
|
||||
#else
|
||||
parser = malloc(sizeof(struct XML_ParserStruct));
|
||||
if (parser != NULL) {
|
||||
diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c
|
||||
index 644a4952..dabdf0da 100644
|
||||
--- a/tests/alloc_tests.c
|
||||
+++ b/tests/alloc_tests.c
|
||||
@@ -2091,6 +2091,13 @@ START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) {
|
||||
}
|
||||
END_TEST
|
||||
|
||||
+#if XML_GE == 1
|
||||
+static size_t
|
||||
+sizeRecordedFor(void *ptr) {
|
||||
+ return *(size_t *)((char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t));
|
||||
+}
|
||||
+#endif // XML_GE == 1
|
||||
+
|
||||
START_TEST(test_alloc_tracker_size_recorded) {
|
||||
XML_Memory_Handling_Suite memsuite = {malloc, realloc, free};
|
||||
|
||||
@@ -2106,16 +2113,16 @@ START_TEST(test_alloc_tracker_size_recorded) {
|
||||
void *ptr = expat_malloc(parser, 10, -1);
|
||||
|
||||
assert_true(ptr != NULL);
|
||||
- assert_true(*((size_t *)ptr - 1) == 10);
|
||||
+ assert_true(sizeRecordedFor(ptr) == 10);
|
||||
|
||||
assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL);
|
||||
|
||||
- assert_true(*((size_t *)ptr - 1) == 10); // i.e. unchanged
|
||||
+ assert_true(sizeRecordedFor(ptr) == 10); // i.e. unchanged
|
||||
|
||||
ptr = expat_realloc(parser, ptr, 20, -1);
|
||||
|
||||
assert_true(ptr != NULL);
|
||||
- assert_true(*((size_t *)ptr - 1) == 20);
|
||||
+ assert_true(sizeRecordedFor(ptr) == 20);
|
||||
|
||||
expat_free(parser, ptr, -1);
|
||||
#endif
|
||||
37
meta/recipes-core/expat/expat/CVE-2025-59375-22.patch
Normal file
37
meta/recipes-core/expat/expat/CVE-2025-59375-22.patch
Normal file
@@ -0,0 +1,37 @@
|
||||
From 5cc0010ad93868ec03248e4ac814272bc7d607bc Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Fri, 19 Sep 2025 22:50:54 +0200
|
||||
Subject: [PATCH] tests: Fix test guard for test related to allocation tracking
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/5cc0010ad93868ec03248e4ac814272bc7d607bc]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
tests/alloc_tests.c | 14 ++++++--------
|
||||
1 file changed, 6 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c
|
||||
index dabdf0da..045447b0 100644
|
||||
--- a/tests/alloc_tests.c
|
||||
+++ b/tests/alloc_tests.c
|
||||
@@ -2362,14 +2362,12 @@ make_alloc_test_case(Suite *s) {
|
||||
tcase_add_test__ifdef_xml_dtd(
|
||||
tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail);
|
||||
|
||||
- tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_size_recorded);
|
||||
- tcase_add_test__ifdef_xml_dtd(tc_alloc,
|
||||
- test_alloc_tracker_maximum_amplification);
|
||||
- tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_threshold);
|
||||
- tcase_add_test__ifdef_xml_dtd(tc_alloc,
|
||||
- test_alloc_tracker_getbuffer_unlimited);
|
||||
- tcase_add_test__ifdef_xml_dtd(tc_alloc, test_alloc_tracker_api);
|
||||
+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_size_recorded);
|
||||
+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_maximum_amplification);
|
||||
+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_threshold);
|
||||
+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_getbuffer_unlimited);
|
||||
+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_api);
|
||||
|
||||
tcase_add_test(tc_alloc, test_mem_api_cycle);
|
||||
- tcase_add_test__ifdef_xml_dtd(tc_alloc, test_mem_api_unlimited);
|
||||
+ tcase_add_test__if_xml_ge(tc_alloc, test_mem_api_unlimited);
|
||||
}
|
||||
47
meta/recipes-core/expat/expat/CVE-2025-59375-23.patch
Normal file
47
meta/recipes-core/expat/expat/CVE-2025-59375-23.patch
Normal file
@@ -0,0 +1,47 @@
|
||||
From 343594dc344e543acb7478d1283b50b299a1c110 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Fri, 19 Sep 2025 22:46:01 +0200
|
||||
Subject: [PATCH] tests: Add new test test_alloc_tracker_pointer_alignment
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/343594dc344e543acb7478d1283b50b299a1c110]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
tests/alloc_tests.c | 17 +++++++++++++++++
|
||||
1 file changed, 17 insertions(+)
|
||||
|
||||
diff --git a/tests/alloc_tests.c b/tests/alloc_tests.c
|
||||
index 045447b0..5ae6c6a7 100644
|
||||
--- a/tests/alloc_tests.c
|
||||
+++ b/tests/alloc_tests.c
|
||||
@@ -2132,6 +2132,22 @@ START_TEST(test_alloc_tracker_size_recorded) {
|
||||
}
|
||||
END_TEST
|
||||
|
||||
+START_TEST(test_alloc_tracker_pointer_alignment) {
|
||||
+ XML_Parser parser = XML_ParserCreate(NULL);
|
||||
+#if XML_GE == 1
|
||||
+ assert_true(sizeof(long long) >= sizeof(size_t)); // self-test
|
||||
+ long long *const ptr
|
||||
+ = (long long *)expat_malloc(parser, 4 * sizeof(long long), -1);
|
||||
+ ptr[0] = 0LL;
|
||||
+ ptr[1] = 1LL;
|
||||
+ ptr[2] = 2LL;
|
||||
+ ptr[3] = 3LL;
|
||||
+ expat_free(parser, ptr, -1);
|
||||
+#endif
|
||||
+ XML_ParserFree(parser);
|
||||
+}
|
||||
+END_TEST
|
||||
+
|
||||
START_TEST(test_alloc_tracker_maximum_amplification) {
|
||||
if (g_reparseDeferralEnabledDefault == XML_TRUE) {
|
||||
return;
|
||||
@@ -2363,6 +2379,7 @@ make_alloc_test_case(Suite *s) {
|
||||
tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail);
|
||||
|
||||
tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_size_recorded);
|
||||
+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_pointer_alignment);
|
||||
tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_maximum_amplification);
|
||||
tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_threshold);
|
||||
tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_getbuffer_unlimited);
|
||||
36
meta/recipes-core/expat/expat/CVE-2025-59375-24.patch
Normal file
36
meta/recipes-core/expat/expat/CVE-2025-59375-24.patch
Normal file
@@ -0,0 +1,36 @@
|
||||
From 6fe5df59a1229ca647d365a0e3a7e17fee4d4548 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastian Pipping <sebastian@pipping.org>
|
||||
Date: Fri, 19 Sep 2025 23:49:18 +0200
|
||||
Subject: [PATCH] Changes: Document pull request #1047
|
||||
|
||||
CVE: CVE-2025-59375
|
||||
Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/6fe5df59a1229ca647d365a0e3a7e17fee4d4548]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
Changes | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
diff --git a/Changes b/Changes
|
||||
index 706a4ae1..58c222d9 100644
|
||||
--- a/Changes
|
||||
+++ b/Changes
|
||||
@@ -61,6 +61,9 @@ Patches:
|
||||
to the pull request URL could be of help.
|
||||
|
||||
Bug fixes:
|
||||
+ #1046 #1047 Fix alignment of internal allocations for some non-amd64
|
||||
+ architectures (e.g. sparc32); fixes up on the fix to
|
||||
+ CVE-2025-59375 in release 2.7.2 from #1034
|
||||
#980 #989 Restore event pointer behavior from Expat 2.6.4
|
||||
(that the fix to CVE-2024-8176 changed in 2.7.0);
|
||||
affected API functions are:
|
||||
@@ -76,7 +79,9 @@ Patches:
|
||||
|
||||
Special thanks to:
|
||||
Berkay Eren Ürün
|
||||
+ Rolf Eike Beer
|
||||
and
|
||||
+ Clang/GCC UndefinedBehaviorSanitizer
|
||||
Perl XML::Parser
|
||||
|
||||
Security fixes:
|
||||
@@ -16,6 +16,31 @@ SRC_URI = "${GITHUB_BASE_URI}/download/R_${VERSION_TAG}/expat-${PV}.tar.bz2 \
|
||||
file://CVE-2024-8176-03.patch \
|
||||
file://CVE-2024-8176-04.patch \
|
||||
file://CVE-2024-8176-05.patch \
|
||||
file://CVE-2025-59375-00.patch \
|
||||
file://CVE-2025-59375-01.patch \
|
||||
file://CVE-2025-59375-02.patch \
|
||||
file://CVE-2025-59375-03.patch \
|
||||
file://CVE-2025-59375-04.patch \
|
||||
file://CVE-2025-59375-05.patch \
|
||||
file://CVE-2025-59375-06.patch \
|
||||
file://CVE-2025-59375-07.patch \
|
||||
file://CVE-2025-59375-08.patch \
|
||||
file://CVE-2025-59375-09.patch \
|
||||
file://CVE-2025-59375-10.patch \
|
||||
file://CVE-2025-59375-11.patch \
|
||||
file://CVE-2025-59375-12.patch \
|
||||
file://CVE-2025-59375-13.patch \
|
||||
file://CVE-2025-59375-14.patch \
|
||||
file://CVE-2025-59375-15.patch \
|
||||
file://CVE-2025-59375-16.patch \
|
||||
file://CVE-2025-59375-17.patch \
|
||||
file://CVE-2025-59375-18.patch \
|
||||
file://CVE-2025-59375-19.patch \
|
||||
file://CVE-2025-59375-20.patch \
|
||||
file://CVE-2025-59375-21.patch \
|
||||
file://CVE-2025-59375-22.patch \
|
||||
file://CVE-2025-59375-23.patch \
|
||||
file://CVE-2025-59375-24.patch \
|
||||
"
|
||||
|
||||
GITHUB_BASE_URI = "https://github.com/libexpat/libexpat/releases/"
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
From 4dd540505d40babe488404f3174ec39f49a84485 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Catanzaro <mcatanzaro@redhat.com>
|
||||
Date: Mon, 4 Aug 2025 15:10:21 -0500
|
||||
Subject: [PATCH] openssl: properly check return value when writing to BIO
|
||||
objects
|
||||
|
||||
In particular, we will read out of bounds, and then write the invalid
|
||||
memory, if BIO_write() fails when getting the PROP_CERTIFICATE_PEM
|
||||
property. Here we attempt to check the return value, but the check is
|
||||
not correct.
|
||||
|
||||
This also fixes a leak of the BIO in the same place.
|
||||
|
||||
Also add error checking to PROP_SUBJECT_NAME and PROP_ISSUER_NAME, for
|
||||
good measure.
|
||||
|
||||
Fixes #226
|
||||
|
||||
CVE: CVE-2025-60018
|
||||
|
||||
Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/glib-networking/-/commit/4dd540505d40babe488404f3174ec39f49a84485]
|
||||
|
||||
Signed-off-by: Rajeshkumar Ramasamy <rajeshkumar.ramasamy@windriver.com>
|
||||
---
|
||||
tls/openssl/gtlscertificate-openssl.c | 25 +++++++++++++++----------
|
||||
1 file changed, 15 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/tls/openssl/gtlscertificate-openssl.c b/tls/openssl/gtlscertificate-openssl.c
|
||||
index 648f3e8..b536559 100644
|
||||
--- a/tls/openssl/gtlscertificate-openssl.c
|
||||
+++ b/tls/openssl/gtlscertificate-openssl.c
|
||||
@@ -362,15 +362,12 @@ g_tls_certificate_openssl_get_property (GObject *object,
|
||||
case PROP_CERTIFICATE_PEM:
|
||||
bio = BIO_new (BIO_s_mem ());
|
||||
|
||||
- if (!PEM_write_bio_X509 (bio, openssl->cert) || !BIO_write (bio, "\0", 1))
|
||||
- certificate_pem = NULL;
|
||||
- else
|
||||
+ if (PEM_write_bio_X509 (bio, openssl->cert) == 1 && BIO_write (bio, "\0", 1) == 1)
|
||||
{
|
||||
BIO_get_mem_data (bio, &certificate_pem);
|
||||
g_value_set_string (value, certificate_pem);
|
||||
-
|
||||
- BIO_free_all (bio);
|
||||
}
|
||||
+ BIO_free_all (bio);
|
||||
break;
|
||||
|
||||
case PROP_PRIVATE_KEY:
|
||||
@@ -411,8 +408,12 @@ g_tls_certificate_openssl_get_property (GObject *object,
|
||||
case PROP_SUBJECT_NAME:
|
||||
bio = BIO_new (BIO_s_mem ());
|
||||
name = X509_get_subject_name (openssl->cert);
|
||||
- X509_NAME_print_ex (bio, name, 0, XN_FLAG_SEP_COMMA_PLUS);
|
||||
- BIO_write (bio, "\0", 1);
|
||||
+ if (X509_NAME_print_ex (bio, name, 0, XN_FLAG_SEP_COMMA_PLUS) < 0 ||
|
||||
+ BIO_write (bio, "\0", 1) != 1)
|
||||
+ {
|
||||
+ BIO_free_all (bio);
|
||||
+ break;
|
||||
+ }
|
||||
BIO_get_mem_data (bio, (char **)&name_string);
|
||||
g_value_set_string (value, name_string);
|
||||
BIO_free_all (bio);
|
||||
@@ -421,9 +422,13 @@ g_tls_certificate_openssl_get_property (GObject *object,
|
||||
case PROP_ISSUER_NAME:
|
||||
bio = BIO_new (BIO_s_mem ());
|
||||
name = X509_get_issuer_name (openssl->cert);
|
||||
- X509_NAME_print_ex (bio, name, 0, XN_FLAG_SEP_COMMA_PLUS);
|
||||
- BIO_write (bio, "\0", 1);
|
||||
- BIO_get_mem_data (bio, &name_string);
|
||||
+ if (X509_NAME_print_ex (bio, name, 0, XN_FLAG_SEP_COMMA_PLUS) < 0 ||
|
||||
+ BIO_write (bio, "\0", 1) != 1)
|
||||
+ {
|
||||
+ BIO_free_all (bio);
|
||||
+ break;
|
||||
+ }
|
||||
+ BIO_get_mem_data (bio, (char **)&name_string);
|
||||
g_value_set_string (value, name_string);
|
||||
BIO_free_all (bio);
|
||||
break;
|
||||
--
|
||||
2.48.1
|
||||
@@ -0,0 +1,147 @@
|
||||
From 70df675dd4f5e4a593b2f95406c1aac031aa8bc7 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Catanzaro <mcatanzaro@redhat.com>
|
||||
Date: Thu, 21 Aug 2025 17:21:01 -0500
|
||||
Subject: [PATCH] openssl: check return values of BIO_new()
|
||||
|
||||
We probably need to check even more return values of even more OpenSSL
|
||||
functions, but these ones allocate memory and that's particularly
|
||||
important to get right.
|
||||
|
||||
CVE: CVE-2025-60019
|
||||
|
||||
Upstream-Status: Backport [https://gitlab.gnome.org/GNOME/glib-networking/-/commit/70df675dd4f5e4a593b2f95406c1aac031aa8bc7]
|
||||
|
||||
Signed-off-by: Rajeshkumar Ramasamy <rajeshkumar.ramasamy@windriver.com>
|
||||
---
|
||||
tls/openssl/gtlscertificate-openssl.c | 42 ++++++++++++++++++++-------
|
||||
1 file changed, 32 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/tls/openssl/gtlscertificate-openssl.c b/tls/openssl/gtlscertificate-openssl.c
|
||||
index b536559..4fa5286 100644
|
||||
--- a/tls/openssl/gtlscertificate-openssl.c
|
||||
+++ b/tls/openssl/gtlscertificate-openssl.c
|
||||
@@ -166,6 +166,9 @@ export_privkey_to_der (GTlsCertificateOpenssl *openssl,
|
||||
goto err;
|
||||
|
||||
bio = BIO_new (BIO_s_mem ());
|
||||
+ if (!bio)
|
||||
+ goto err;
|
||||
+
|
||||
if (i2d_PKCS8_PRIV_KEY_INFO_bio (bio, pkcs8) == 0)
|
||||
goto err;
|
||||
|
||||
@@ -199,6 +202,9 @@ export_privkey_to_pem (GTlsCertificateOpenssl *openssl)
|
||||
return NULL;
|
||||
|
||||
bio = BIO_new (BIO_s_mem ());
|
||||
+ if (!bio)
|
||||
+ goto out;
|
||||
+
|
||||
ret = PEM_write_bio_PKCS8PrivateKey (bio, openssl->key, NULL, NULL, 0, NULL, NULL);
|
||||
if (ret == 0)
|
||||
goto out;
|
||||
@@ -211,7 +217,7 @@ export_privkey_to_pem (GTlsCertificateOpenssl *openssl)
|
||||
result = g_strdup (data);
|
||||
|
||||
out:
|
||||
- BIO_free_all (bio);
|
||||
+ g_clear_pointer (&bio, BIO_free_all);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -232,6 +238,9 @@ maybe_import_pkcs12 (GTlsCertificateOpenssl *openssl)
|
||||
return;
|
||||
|
||||
bio = BIO_new (BIO_s_mem ());
|
||||
+ if (!bio)
|
||||
+ goto import_failed;
|
||||
+
|
||||
status = BIO_write (bio, openssl->pkcs12_data->data, openssl->pkcs12_data->len);
|
||||
if (status <= 0)
|
||||
goto import_failed;
|
||||
@@ -323,7 +332,7 @@ g_tls_certificate_openssl_get_property (GObject *object,
|
||||
guint8 *data;
|
||||
BIO *bio;
|
||||
GByteArray *byte_array;
|
||||
- char *certificate_pem;
|
||||
+ const char *certificate_pem;
|
||||
long size;
|
||||
|
||||
const ASN1_TIME *time_asn1;
|
||||
@@ -362,12 +371,12 @@ g_tls_certificate_openssl_get_property (GObject *object,
|
||||
case PROP_CERTIFICATE_PEM:
|
||||
bio = BIO_new (BIO_s_mem ());
|
||||
|
||||
- if (PEM_write_bio_X509 (bio, openssl->cert) == 1 && BIO_write (bio, "\0", 1) == 1)
|
||||
+ if (bio && PEM_write_bio_X509 (bio, openssl->cert) == 1 && BIO_write (bio, "\0", 1) == 1)
|
||||
{
|
||||
BIO_get_mem_data (bio, &certificate_pem);
|
||||
g_value_set_string (value, certificate_pem);
|
||||
}
|
||||
- BIO_free_all (bio);
|
||||
+ g_clear_pointer (&bio, BIO_free_all);
|
||||
break;
|
||||
|
||||
case PROP_PRIVATE_KEY:
|
||||
@@ -407,6 +416,8 @@ g_tls_certificate_openssl_get_property (GObject *object,
|
||||
|
||||
case PROP_SUBJECT_NAME:
|
||||
bio = BIO_new (BIO_s_mem ());
|
||||
+ if (!bio)
|
||||
+ break;
|
||||
name = X509_get_subject_name (openssl->cert);
|
||||
if (X509_NAME_print_ex (bio, name, 0, XN_FLAG_SEP_COMMA_PLUS) < 0 ||
|
||||
BIO_write (bio, "\0", 1) != 1)
|
||||
@@ -421,6 +432,8 @@ g_tls_certificate_openssl_get_property (GObject *object,
|
||||
|
||||
case PROP_ISSUER_NAME:
|
||||
bio = BIO_new (BIO_s_mem ());
|
||||
+ if (!bio)
|
||||
+ break;
|
||||
name = X509_get_issuer_name (openssl->cert);
|
||||
if (X509_NAME_print_ex (bio, name, 0, XN_FLAG_SEP_COMMA_PLUS) < 0 ||
|
||||
BIO_write (bio, "\0", 1) != 1)
|
||||
@@ -533,8 +546,11 @@ g_tls_certificate_openssl_set_property (GObject *object,
|
||||
break;
|
||||
CRITICAL_IF_CERTIFICATE_INITIALIZED ("certificate-pem");
|
||||
bio = BIO_new_mem_buf ((gpointer)string, -1);
|
||||
- openssl->cert = PEM_read_bio_X509 (bio, NULL, NULL, NULL);
|
||||
- BIO_free (bio);
|
||||
+ if (bio)
|
||||
+ {
|
||||
+ openssl->cert = PEM_read_bio_X509 (bio, NULL, NULL, NULL);
|
||||
+ BIO_free (bio);
|
||||
+ }
|
||||
if (openssl->cert)
|
||||
openssl->have_cert = TRUE;
|
||||
else if (!openssl->construct_error)
|
||||
@@ -554,8 +570,11 @@ g_tls_certificate_openssl_set_property (GObject *object,
|
||||
CRITICAL_IF_KEY_INITIALIZED ("private-key");
|
||||
|
||||
bio = BIO_new_mem_buf (bytes->data, bytes->len);
|
||||
- openssl->key = d2i_PrivateKey_bio (bio, NULL);
|
||||
- BIO_free (bio);
|
||||
+ if (bio)
|
||||
+ {
|
||||
+ openssl->key = d2i_PrivateKey_bio (bio, NULL);
|
||||
+ BIO_free (bio);
|
||||
+ }
|
||||
if (openssl->key)
|
||||
openssl->have_key = TRUE;
|
||||
else if (!openssl->construct_error)
|
||||
@@ -575,8 +594,11 @@ g_tls_certificate_openssl_set_property (GObject *object,
|
||||
CRITICAL_IF_KEY_INITIALIZED ("private-key-pem");
|
||||
|
||||
bio = BIO_new_mem_buf ((gpointer)string, -1);
|
||||
- openssl->key = PEM_read_bio_PrivateKey (bio, NULL, NULL, NULL);
|
||||
- BIO_free (bio);
|
||||
+ if (bio)
|
||||
+ {
|
||||
+ openssl->key = PEM_read_bio_PrivateKey (bio, NULL, NULL, NULL);
|
||||
+ BIO_free (bio);
|
||||
+ }
|
||||
if (openssl->key)
|
||||
openssl->have_key = TRUE;
|
||||
else if (!openssl->construct_error)
|
||||
--
|
||||
2.48.1
|
||||
@@ -31,6 +31,8 @@ inherit gnomebase gettext upstream-version-is-even gio-module-cache ptest-gnome
|
||||
|
||||
SRC_URI += "file://run-ptest"
|
||||
SRC_URI += "file://eagain.patch"
|
||||
SRC_URI += "file://CVE-2025-60018.patch"
|
||||
SRC_URI += "file://CVE-2025-60019.patch"
|
||||
|
||||
FILES:${PN} += "\
|
||||
${libdir}/gio/modules/libgio*.so \
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
SRCBRANCH ?= "release/2.39/master"
|
||||
PV = "2.39+git"
|
||||
SRCREV_glibc ?= "b027d5b145f1b2908f370bdb96dfe40180d0fcb6"
|
||||
SRCREV_localedef ?= "fab74f31b3811df543e24b6de47efdf45b538abc"
|
||||
SRCREV_glibc ?= "58cbbd43fe82910cf8ae9008351b0b0665104500"
|
||||
SRCREV_localedef ?= "cba02c503d7c853a38ccfb83c57e343ca5ecd7e5"
|
||||
|
||||
GLIBC_GIT_URI ?= "git://sourceware.org/git/glibc.git;protocol=https"
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ inherit core-image setuptools3 features_check
|
||||
|
||||
REQUIRED_DISTRO_FEATURES += "xattr"
|
||||
|
||||
SRCREV ?= "517a1206e0e7fbb5d0f05b25a08b0f06462a4b8c"
|
||||
SRCREV ?= "dd2d3cfc4e26fdb9a3105ddf0af040f5a29b6306"
|
||||
SRC_URI = "git://git.yoctoproject.org/poky;branch=scarthgap \
|
||||
file://Yocto_Build_Appliance.vmx \
|
||||
file://Yocto_Build_Appliance.vmxf \
|
||||
|
||||
@@ -43,6 +43,7 @@ SRC_URI = "${KERNELORG_MIRROR}/linux/utils/util-linux/v${MAJOR_VERSION}/util-lin
|
||||
file://CVE-2024-28085-0001.patch \
|
||||
file://CVE-2024-28085-0002.patch \
|
||||
file://fstab-isolation.patch \
|
||||
file://sys-utils-hwclock-rtc-fix-pointer-usage.patch \
|
||||
"
|
||||
|
||||
SRC_URI[sha256sum] = "7b6605e48d1a49f43cc4b4cfc59f313d0dd5402fa40b96810bd572e167dfed0f"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
From 7064cd275607a43223b2dbaef75c610f33f432ff Mon Sep 17 00:00:00 2001
|
||||
From: Karthikeyan Krishnasamy <karthikeyan@linumiz.com>
|
||||
Date: Sat, 23 Mar 2024 13:39:55 +0530
|
||||
Subject: [PATCH] sys-utils: hwclock-rtc: fix pointer usage
|
||||
|
||||
passing double pointer doesn't fill param value
|
||||
|
||||
Signed-off-by: Karthikeyan Krishnasamy <karthikeyan@linumiz.com>
|
||||
Upstream-Status: Backport [https://github.com/util-linux/util-linux/commit/1064a53e4ff357dc649a8c4a0a41dfb5a1191bba]
|
||||
Signed-off-by: Bastian Krause <bst@pengutronix.de>
|
||||
---
|
||||
sys-utils/hwclock-rtc.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/sys-utils/hwclock-rtc.c b/sys-utils/hwclock-rtc.c
|
||||
index 7094cd0..c797397 100644
|
||||
--- a/sys-utils/hwclock-rtc.c
|
||||
+++ b/sys-utils/hwclock-rtc.c
|
||||
@@ -424,7 +424,7 @@ static int resolve_rtc_param_alias(const char *alias, __u64 *value)
|
||||
/* kernel uapi __u64 can be defined differently than uint64_t */
|
||||
static int strtoku64(const char *str, __u64 *num, int base)
|
||||
{
|
||||
- return ul_strtou64(str, (uint64_t *) &num, base);
|
||||
+ return ul_strtou64(str, (uint64_t *) num, base);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -59,5 +59,12 @@ SRC_URI = "\
|
||||
file://0022-CVE-2025-5244.patch \
|
||||
file://0023-CVE-2025-7546.patch \
|
||||
file://0023-CVE-2025-7545.patch \
|
||||
file://0024-CVE-2025-11082.patch \
|
||||
file://0025-CVE-2025-11083.patch \
|
||||
file://0026-CVE-2025-11081.patch \
|
||||
file://0027-CVE-2025-8225.patch \
|
||||
file://CVE-2025-11414.patch \
|
||||
file://CVE-2025-11412.patch \
|
||||
file://CVE-2025-11413.patch \
|
||||
"
|
||||
S = "${WORKDIR}/git"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
From ea1a0737c7692737a644af0486b71e4a392cbca8 Mon Sep 17 00:00:00 2001
|
||||
From: "H.J. Lu" <hjl.tools@gmail.com>
|
||||
Date: Mon, 22 Sep 2025 15:20:34 +0800
|
||||
Subject: [PATCH] elf: Don't read beyond .eh_frame section size
|
||||
|
||||
PR ld/33464
|
||||
* elf-eh-frame.c (_bfd_elf_parse_eh_frame): Don't read beyond
|
||||
.eh_frame section size.
|
||||
|
||||
Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
|
||||
|
||||
CVE: CVE-2025-11082
|
||||
Upstream-Status: Backport [https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=ea1a0737c7692737a644af0486b71e4a392cbca8]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
bfd/elf-eh-frame.c | 8 ++++++--
|
||||
1 file changed, 6 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c
|
||||
index dc0d2e097f5..30bb313489c 100644
|
||||
--- a/bfd/elf-eh-frame.c
|
||||
+++ b/bfd/elf-eh-frame.c
|
||||
@@ -734,6 +734,7 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
|
||||
if (hdr_id == 0)
|
||||
{
|
||||
unsigned int initial_insn_length;
|
||||
+ char *null_byte;
|
||||
|
||||
/* CIE */
|
||||
this_inf->cie = 1;
|
||||
@@ -750,10 +751,13 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
|
||||
REQUIRE (cie->version == 1
|
||||
|| cie->version == 3
|
||||
|| cie->version == 4);
|
||||
- REQUIRE (strlen ((char *) buf) < sizeof (cie->augmentation));
|
||||
+ null_byte = memchr ((char *) buf, 0, end - buf);
|
||||
+ REQUIRE (null_byte != NULL);
|
||||
+ REQUIRE ((size_t) (null_byte - (char *) buf)
|
||||
+ < sizeof (cie->augmentation));
|
||||
|
||||
strcpy (cie->augmentation, (char *) buf);
|
||||
- buf = (bfd_byte *) strchr ((char *) buf, '\0') + 1;
|
||||
+ buf = (bfd_byte *) null_byte + 1;
|
||||
this_inf->u.cie.aug_str_len = buf - start - 1;
|
||||
ENSURE_NO_RELOCS (buf);
|
||||
if (buf[0] == 'e' && buf[1] == 'h')
|
||||
@@ -0,0 +1,77 @@
|
||||
From 9ca499644a21ceb3f946d1c179c38a83be084490 Mon Sep 17 00:00:00 2001
|
||||
From: "H.J. Lu" <hjl.tools@gmail.com>
|
||||
Date: Thu, 18 Sep 2025 16:59:25 -0700
|
||||
Subject: [PATCH] elf: Don't match corrupt section header in linker input
|
||||
|
||||
Don't swap in nor match corrupt section header in linker input to avoid
|
||||
linker crash later.
|
||||
|
||||
PR ld/33457
|
||||
* elfcode.h (elf_swap_shdr_in): Changed to return bool. Return
|
||||
false for corrupt section header in linker input.
|
||||
(elf_object_p): Reject if elf_swap_shdr_in returns false.
|
||||
|
||||
Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
|
||||
|
||||
CVE: CVE-2025-11083
|
||||
Upstream-Status: Backport [https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=9ca499644a21ceb3f946d1c179c38a83be084490]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
bfd/elfcode.h | 14 +++++++++-----
|
||||
1 file changed, 9 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/bfd/elfcode.h b/bfd/elfcode.h
|
||||
index 9c65852e103..5224a1abee6 100644
|
||||
--- a/bfd/elfcode.h
|
||||
+++ b/bfd/elfcode.h
|
||||
@@ -311,7 +311,7 @@ elf_swap_ehdr_out (bfd *abfd,
|
||||
/* Translate an ELF section header table entry in external format into an
|
||||
ELF section header table entry in internal format. */
|
||||
|
||||
-static void
|
||||
+static bool
|
||||
elf_swap_shdr_in (bfd *abfd,
|
||||
const Elf_External_Shdr *src,
|
||||
Elf_Internal_Shdr *dst)
|
||||
@@ -341,6 +341,9 @@ elf_swap_shdr_in (bfd *abfd,
|
||||
{
|
||||
_bfd_error_handler (_("warning: %pB has a section "
|
||||
"extending past end of file"), abfd);
|
||||
+ /* PR ld/33457: Don't match corrupt section header. */
|
||||
+ if (abfd->is_linker_input)
|
||||
+ return false;
|
||||
abfd->read_only = 1;
|
||||
}
|
||||
}
|
||||
@@ -350,6 +353,7 @@ elf_swap_shdr_in (bfd *abfd,
|
||||
dst->sh_entsize = H_GET_WORD (abfd, src->sh_entsize);
|
||||
dst->bfd_section = NULL;
|
||||
dst->contents = NULL;
|
||||
+ return true;
|
||||
}
|
||||
|
||||
/* Translate an ELF section header table entry in internal format into an
|
||||
@@ -642,9 +646,9 @@ elf_object_p (bfd *abfd)
|
||||
|
||||
/* Read the first section header at index 0, and convert to internal
|
||||
form. */
|
||||
- if (bfd_read (&x_shdr, sizeof x_shdr, abfd) != sizeof (x_shdr))
|
||||
+ if (bfd_read (&x_shdr, sizeof x_shdr, abfd) != sizeof (x_shdr)
|
||||
+ || !elf_swap_shdr_in (abfd, &x_shdr, &i_shdr))
|
||||
goto got_no_match;
|
||||
- elf_swap_shdr_in (abfd, &x_shdr, &i_shdr);
|
||||
|
||||
/* If the section count is zero, the actual count is in the first
|
||||
section header. */
|
||||
@@ -730,9 +734,9 @@ elf_object_p (bfd *abfd)
|
||||
to internal form. */
|
||||
for (shindex = 1; shindex < i_ehdrp->e_shnum; shindex++)
|
||||
{
|
||||
- if (bfd_read (&x_shdr, sizeof x_shdr, abfd) != sizeof (x_shdr))
|
||||
+ if (bfd_read (&x_shdr, sizeof x_shdr, abfd) != sizeof (x_shdr)
|
||||
+ || !elf_swap_shdr_in (abfd, &x_shdr, i_shdrp + shindex))
|
||||
goto got_no_match;
|
||||
- elf_swap_shdr_in (abfd, &x_shdr, i_shdrp + shindex);
|
||||
|
||||
/* Sanity check sh_link and sh_info. */
|
||||
if (i_shdrp[shindex].sh_link >= num_sec)
|
||||
@@ -0,0 +1,84 @@
|
||||
From f87a66db645caf8cc0e6fc87b0c28c78a38af59b Mon Sep 17 00:00:00 2001
|
||||
From: Alan Modra <amodra@gmail.com>
|
||||
Date: Tue, 9 Sep 2025 18:32:09 +0930
|
||||
Subject: [PATCH] PR 33406 SEGV in dump_dwarf_section
|
||||
|
||||
Trying to dump .sframe in a PE file results in a segfault accessing
|
||||
elf_section_data.
|
||||
|
||||
* objdump (dump_sframe_section, dump_dwarf_section): Don't access
|
||||
elf_section_type without first checking the file is ELF.
|
||||
---
|
||||
binutils/objdump.c | 10 ++++++----
|
||||
1 file changed, 6 insertions(+), 4 deletions(-)
|
||||
|
||||
Upstream-Status: Backport [https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=f87a66db645caf8cc0e6fc87b0c28c78a38af59b]
|
||||
CVE: CVE-2025-11081
|
||||
|
||||
Signed-off-by: Alan Modra <amodra@gmail.com>
|
||||
Signed-off-by: Yash Shinde <Yash.Shinde@windriver.com>
|
||||
|
||||
diff --git a/binutils/objdump.c b/binutils/objdump.c
|
||||
index 290f7e51f66..ee8823da05a 100644
|
||||
--- a/binutils/objdump.c
|
||||
+++ b/binutils/objdump.c
|
||||
@@ -4418,6 +4418,10 @@
|
||||
else
|
||||
match = name;
|
||||
|
||||
+ if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
|
||||
+ && elf_section_type (section) == SHT_GNU_SFRAME)
|
||||
+ match = ".sframe";
|
||||
+
|
||||
for (i = 0; i < max; i++)
|
||||
if ((strcmp (debug_displays [i].section.uncompressed_name, match) == 0
|
||||
|| strcmp (debug_displays [i].section.compressed_name, match) == 0
|
||||
@@ -4923,6 +4927,36 @@
|
||||
}
|
||||
|
||||
+static void
|
||||
+dump_sframe_section (bfd *abfd, const char *sect_name, bool is_mainfile)
|
||||
+
|
||||
+{
|
||||
+ /* Error checking for user provided SFrame section name, if any. */
|
||||
+ if (sect_name)
|
||||
+ {
|
||||
+ asection *sec = bfd_get_section_by_name (abfd, sect_name);
|
||||
+ if (sec == NULL)
|
||||
+ {
|
||||
+ printf (_("No %s section present\n\n"), sanitize_string (sect_name));
|
||||
+ return;
|
||||
+ }
|
||||
+ /* Starting with Binutils 2.45, SFrame sections have section type
|
||||
+ SHT_GNU_SFRAME. For SFrame sections from Binutils 2.44 or earlier,
|
||||
+ check explcitly for SFrame sections of type SHT_PROGBITS and name
|
||||
+ ".sframe" to allow them. */
|
||||
+ else if (bfd_get_flavour (abfd) != bfd_target_elf_flavour
|
||||
+ || (elf_section_type (sec) != SHT_GNU_SFRAME
|
||||
+ && !(elf_section_type (sec) == SHT_PROGBITS
|
||||
+ && strcmp (sect_name, ".sframe") == 0)))
|
||||
+ {
|
||||
+ printf (_("Section %s does not contain SFrame data\n\n"),
|
||||
+ sanitize_string (sect_name));
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+ dump_dwarf (abfd, is_mainfile);
|
||||
+}
|
||||
+
|
||||
static void
|
||||
dump_target_specific (bfd *abfd)
|
||||
{
|
||||
const struct objdump_private_desc * const *desc;
|
||||
diff --git a/include/elf/common.h b/include/elf/common.h
|
||||
--- a/include/elf/common.h
|
||||
+++ b/include/elf/common.h
|
||||
@@ -528,6 +528,8 @@
|
||||
#define SHT_LOOS 0x60000000 /* First of OS specific semantics */
|
||||
#define SHT_HIOS 0x6fffffff /* Last of OS specific semantics */
|
||||
|
||||
+#define SHT_GNU_SFRAME 0x6ffffff4 /* SFrame stack trace information. */
|
||||
+
|
||||
#define SHT_GNU_INCREMENTAL_INPUTS 0x6fff4700 /* incremental build data */
|
||||
#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes */
|
||||
#define SHT_GNU_HASH 0x6ffffff6 /* GNU style symbol hash table */
|
||||
@@ -0,0 +1,47 @@
|
||||
From e51fdff7d2e538c0e5accdd65649ac68e6e0ddd4 Mon Sep 17 00:00:00 2001
|
||||
From: Alan Modra <amodra@gmail.com>
|
||||
Date: Wed, 19 Feb 2025 22:45:29 +1030
|
||||
Subject: [PATCH] binutils/dwarf.c debug_information leak
|
||||
|
||||
It is possible with fuzzed files to have num_debug_info_entries zero
|
||||
after allocating space for debug_information, leading to multiple
|
||||
allocations.
|
||||
|
||||
* dwarf.c (process_debug_info): Don't test num_debug_info_entries
|
||||
to determine whether debug_information has been allocated,
|
||||
test alloc_num_debug_info_entries.
|
||||
---
|
||||
|
||||
Upstream-Status: Backport [https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=e51fdff7d2e538c0e5accdd65649ac68e6e0ddd4]
|
||||
CVE: CVE-2025-8225
|
||||
|
||||
binutils/dwarf.c | 8 +++-----
|
||||
1 file changed, 3 insertions(+), 5 deletions(-)
|
||||
|
||||
Signed-off-by: Alan Modra <amodra@gmail.com>
|
||||
Signed-off-by: Yash Shinde <Yash.Shinde@windriver.com>
|
||||
|
||||
diff --git a/binutils/dwarf.c b/binutils/dwarf.c
|
||||
index 8e004cea839..bfbf83ec9f4 100644
|
||||
--- a/binutils/dwarf.c
|
||||
+++ b/binutils/dwarf.c
|
||||
@@ -3807,13 +3807,11 @@ process_debug_info (struct dwarf_section * section,
|
||||
}
|
||||
|
||||
if ((do_loc || do_debug_loc || do_debug_ranges || do_debug_info)
|
||||
- && num_debug_info_entries == 0
|
||||
- && ! do_types)
|
||||
+ && alloc_num_debug_info_entries == 0
|
||||
+ && !do_types)
|
||||
{
|
||||
-
|
||||
/* Then allocate an array to hold the information. */
|
||||
- debug_information = (debug_info *) cmalloc (num_units,
|
||||
- sizeof (* debug_information));
|
||||
+ debug_information = cmalloc (num_units, sizeof (*debug_information));
|
||||
if (debug_information == NULL)
|
||||
{
|
||||
error (_("Not enough memory for a debug info array of %u entries\n"),
|
||||
--
|
||||
2.43.7
|
||||
|
||||
35
meta/recipes-devtools/binutils/binutils/CVE-2025-11412.patch
Normal file
35
meta/recipes-devtools/binutils/binutils/CVE-2025-11412.patch
Normal file
@@ -0,0 +1,35 @@
|
||||
From 047435dd988a3975d40c6626a8f739a0b2e154bc Mon Sep 17 00:00:00 2001
|
||||
From: Alan Modra <amodra@gmail.com>
|
||||
Date: Thu, 25 Sep 2025 08:22:24 +0930
|
||||
Subject: [PATCH] PR 33452 SEGV in bfd_elf_gc_record_vtentry
|
||||
|
||||
Limit addends on vtentry relocs, otherwise ld might attempt to
|
||||
allocate a stupidly large array. This also fixes the expression
|
||||
overflow leading to pr33452. A vtable of 33M entries on a 64-bit
|
||||
host is surely large enough, especially considering that VTINHERIT
|
||||
and VTENTRY relocations are to support -fvtable-gc that disappeared
|
||||
from gcc over 20 years ago.
|
||||
|
||||
PR ld/33452
|
||||
* elflink.c (bfd_elf_gc_record_vtentry): Sanity check addend.
|
||||
|
||||
CVE: CVE-2025-11412
|
||||
Upstream-Status: Backport [https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=047435dd988a3975d40c6626a8f739a0b2e154bc]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
bfd/elflink.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/bfd/elflink.c b/bfd/elflink.c
|
||||
index 54f0d6e957e..0a0456177c2 100644
|
||||
--- a/bfd/elflink.c
|
||||
+++ b/bfd/elflink.c
|
||||
@@ -14613,7 +14613,7 @@ bfd_elf_gc_record_vtentry (bfd *abfd, asection *sec,
|
||||
const struct elf_backend_data *bed = get_elf_backend_data (abfd);
|
||||
unsigned int log_file_align = bed->s->log_file_align;
|
||||
|
||||
- if (!h)
|
||||
+ if (!h || addend > 1u << 28)
|
||||
{
|
||||
/* xgettext:c-format */
|
||||
_bfd_error_handler (_("%pB: section '%pA': corrupt VTENTRY entry"),
|
||||
38
meta/recipes-devtools/binutils/binutils/CVE-2025-11413.patch
Normal file
38
meta/recipes-devtools/binutils/binutils/CVE-2025-11413.patch
Normal file
@@ -0,0 +1,38 @@
|
||||
From 72efdf166aa0ed72ecc69fc2349af6591a7a19c0 Mon Sep 17 00:00:00 2001
|
||||
From: Alan Modra <amodra@gmail.com>
|
||||
Date: Thu, 25 Sep 2025 10:41:32 +0930
|
||||
Subject: [PATCH] Re: elf: Disallow the empty global symbol name
|
||||
|
||||
sparc64-linux-gnu +FAIL: selective2
|
||||
sparc64-linux-gnu +FAIL: selective3
|
||||
|
||||
PR ld/33456
|
||||
* elflink.c (elf_link_add_object_symbols): Move new check later
|
||||
to give the backend add_symbol_hook a chance to remove symbols
|
||||
with empty names.
|
||||
|
||||
CVE: CVE-2025-11413
|
||||
Upstream-Status: Backport [https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=72efdf166aa0ed72ecc69fc2349af6591a7a19c0]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
bfd/elflink.c | 7 +++++++
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
diff --git a/bfd/elflink.c b/bfd/elflink.c
|
||||
index 0a0456177c2..5c8b822e36a 100644
|
||||
--- a/bfd/elflink.c
|
||||
+++ b/bfd/elflink.c
|
||||
@@ -5015,6 +5015,13 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
|
||||
continue;
|
||||
}
|
||||
|
||||
+ if (name[0] == '\0')
|
||||
+ {
|
||||
+ _bfd_error_handler (_("%pB: corrupt symbol table"), abfd);
|
||||
+ bfd_set_error (bfd_error_bad_value);
|
||||
+ goto error_free_vers;
|
||||
+ }
|
||||
+
|
||||
/* Sanity check that all possibilities were handled. */
|
||||
if (sec == NULL)
|
||||
abort ();
|
||||
84
meta/recipes-devtools/binutils/binutils/CVE-2025-11414.patch
Normal file
84
meta/recipes-devtools/binutils/binutils/CVE-2025-11414.patch
Normal file
@@ -0,0 +1,84 @@
|
||||
From aeaaa9af6359c8e394ce9cf24911fec4f4d23703 Mon Sep 17 00:00:00 2001
|
||||
From: "H.J. Lu" <hjl.tools@gmail.com>
|
||||
Date: Tue, 23 Sep 2025 08:52:26 +0800
|
||||
Subject: [PATCH] elf: Return error on unsorted symbol table if not allowed
|
||||
|
||||
Normally ELF symbol table should be sorted, i.e., local symbols precede
|
||||
global symbols. Irix 6 is an exception and its elf_bad_symtab is set
|
||||
to true. Issue an error if elf_bad_symtab is false and symbol table is
|
||||
unsorted.
|
||||
|
||||
PR ld/33450
|
||||
* elflink.c (set_symbol_value): Change return type to bool and
|
||||
return false on error. Issue an error on unsorted symbol table
|
||||
if not allowed.
|
||||
(elf_link_input_bfd): Return false if set_symbol_value reurns
|
||||
false.
|
||||
|
||||
Signed-off-by: H.J. Lu <hjl.tools@gmail.com>
|
||||
|
||||
CVE: CVE-2025-11414
|
||||
Upstream-Status: Backport [https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=aeaaa9af6359c8e394ce9cf24911fec4f4d23703]
|
||||
Signed-off-by: Peter Marko <peter.marko@siemens.com>
|
||||
---
|
||||
bfd/elflink.c | 21 +++++++++++++++------
|
||||
1 file changed, 15 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/bfd/elflink.c b/bfd/elflink.c
|
||||
index 66982f82b94..54f0d6e957e 100644
|
||||
--- a/bfd/elflink.c
|
||||
+++ b/bfd/elflink.c
|
||||
@@ -8914,7 +8914,7 @@ struct elf_outext_info
|
||||
<binary-operator> := as in C
|
||||
<unary-operator> := as in C, plus "0-" for unambiguous negation. */
|
||||
|
||||
-static void
|
||||
+static bool
|
||||
set_symbol_value (bfd *bfd_with_globals,
|
||||
Elf_Internal_Sym *isymbuf,
|
||||
size_t locsymcount,
|
||||
@@ -8935,9 +8935,15 @@ set_symbol_value (bfd *bfd_with_globals,
|
||||
"absolute" section and give it a value. */
|
||||
sym->st_shndx = SHN_ABS;
|
||||
sym->st_value = val;
|
||||
- return;
|
||||
+ return true;
|
||||
+ }
|
||||
+ if (!elf_bad_symtab (bfd_with_globals))
|
||||
+ {
|
||||
+ _bfd_error_handler (_("%pB: corrupt symbol table"),
|
||||
+ bfd_with_globals);
|
||||
+ bfd_set_error (bfd_error_bad_value);
|
||||
+ return false;
|
||||
}
|
||||
- BFD_ASSERT (elf_bad_symtab (bfd_with_globals));
|
||||
extsymoff = 0;
|
||||
}
|
||||
|
||||
@@ -8947,11 +8953,12 @@ set_symbol_value (bfd *bfd_with_globals,
|
||||
if (h == NULL)
|
||||
{
|
||||
/* FIXMEL What should we do ? */
|
||||
- return;
|
||||
+ return false;
|
||||
}
|
||||
h->root.type = bfd_link_hash_defined;
|
||||
h->root.u.def.value = val;
|
||||
h->root.u.def.section = bfd_abs_section_ptr;
|
||||
+ return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -11641,8 +11648,10 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd)
|
||||
return false;
|
||||
|
||||
/* Symbol evaluated OK. Update to absolute value. */
|
||||
- set_symbol_value (input_bfd, isymbuf, locsymcount,
|
||||
- r_symndx, val);
|
||||
+ if (!set_symbol_value (input_bfd, isymbuf, locsymcount, r_symndx,
|
||||
+ val))
|
||||
+ return false;
|
||||
+
|
||||
continue;
|
||||
}
|
||||
|
||||
71
meta/recipes-devtools/cmake/cmake/CVE-2025-9301.patch
Normal file
71
meta/recipes-devtools/cmake/cmake/CVE-2025-9301.patch
Normal file
@@ -0,0 +1,71 @@
|
||||
From 37e27f71bc356d880c908040cd0cb68fa2c371b8 Mon Sep 17 00:00:00 2001
|
||||
From: Tyler Yankee <tyler.yankee@kitware.com>
|
||||
Date: Wed, 13 Aug 2025 15:22:28 -0400
|
||||
Subject: [PATCH] foreach: Explicitly skip replay without iterations
|
||||
|
||||
As written, foreach loops with a trailing `IN` (i.e., no loop
|
||||
variable(s) given) lead to an assertion error. Handle this case by
|
||||
exiting early when we know the loop won't execute anything.
|
||||
|
||||
Fixes: #27135
|
||||
|
||||
CVE: CVE-2025-9301
|
||||
|
||||
Upstream-Status: Backport
|
||||
https://gitlab.kitware.com/cmake/cmake/-/commit/37e27f71bc356d880c908040cd0cb68fa2c371b8
|
||||
|
||||
Signed-off-by: Tyler Yankee <tyler.yankee@kitware.com>
|
||||
Signed-off-by: Saravanan <saravanan.kadambathursubramaniyam@windriver.com>
|
||||
---
|
||||
Source/cmForEachCommand.cxx | 3 +++
|
||||
Tests/RunCMake/foreach/RunCMakeTest.cmake | 1 +
|
||||
Tests/RunCMake/foreach/TrailingIn-result.txt | 1 +
|
||||
Tests/RunCMake/foreach/TrailingIn.cmake | 5 +++++
|
||||
4 files changed, 10 insertions(+)
|
||||
create mode 100644 Tests/RunCMake/foreach/TrailingIn-result.txt
|
||||
create mode 100644 Tests/RunCMake/foreach/TrailingIn.cmake
|
||||
|
||||
diff --git a/Source/cmForEachCommand.cxx b/Source/cmForEachCommand.cxx
|
||||
index 21a140d0..23f953a5 100644
|
||||
--- a/Source/cmForEachCommand.cxx
|
||||
+++ b/Source/cmForEachCommand.cxx
|
||||
@@ -101,6 +101,9 @@ bool cmForEachFunctionBlocker::ArgumentsMatch(cmListFileFunction const& lff,
|
||||
bool cmForEachFunctionBlocker::Replay(
|
||||
std::vector<cmListFileFunction> functions, cmExecutionStatus& inStatus)
|
||||
{
|
||||
+ if (this->Args.size() == this->IterationVarsCount) {
|
||||
+ return true;
|
||||
+ }
|
||||
return this->ZipLists ? this->ReplayZipLists(functions, inStatus)
|
||||
: this->ReplayItems(functions, inStatus);
|
||||
}
|
||||
diff --git a/Tests/RunCMake/foreach/RunCMakeTest.cmake b/Tests/RunCMake/foreach/RunCMakeTest.cmake
|
||||
index 15ca4770..acfc742e 100644
|
||||
--- a/Tests/RunCMake/foreach/RunCMakeTest.cmake
|
||||
+++ b/Tests/RunCMake/foreach/RunCMakeTest.cmake
|
||||
@@ -22,3 +22,4 @@ run_cmake(foreach-RANGE-invalid-test)
|
||||
run_cmake(foreach-RANGE-out-of-range-test)
|
||||
run_cmake(foreach-var-scope-CMP0124-OLD)
|
||||
run_cmake(foreach-var-scope-CMP0124-NEW)
|
||||
+run_cmake(TrailingIn)
|
||||
diff --git a/Tests/RunCMake/foreach/TrailingIn-result.txt b/Tests/RunCMake/foreach/TrailingIn-result.txt
|
||||
new file mode 100644
|
||||
index 00000000..573541ac
|
||||
--- /dev/null
|
||||
+++ b/Tests/RunCMake/foreach/TrailingIn-result.txt
|
||||
@@ -0,0 +1 @@
|
||||
+0
|
||||
diff --git a/Tests/RunCMake/foreach/TrailingIn.cmake b/Tests/RunCMake/foreach/TrailingIn.cmake
|
||||
new file mode 100644
|
||||
index 00000000..e2b5b2f2
|
||||
--- /dev/null
|
||||
+++ b/Tests/RunCMake/foreach/TrailingIn.cmake
|
||||
@@ -0,0 +1,5 @@
|
||||
+foreach(v IN)
|
||||
+endforeach()
|
||||
+
|
||||
+foreach(v1 v2 IN)
|
||||
+endforeach()
|
||||
--
|
||||
2.44.3
|
||||
|
||||
@@ -11,6 +11,7 @@ SRC_URI:append:class-nativesdk = " \
|
||||
file://cmake-setup.py \
|
||||
file://environment.d-cmake.sh \
|
||||
"
|
||||
SRC_URI += "file://CVE-2025-9301.patch"
|
||||
|
||||
LICENSE:append = " & BSD-1-Clause & MIT"
|
||||
LIC_FILES_CHKSUM:append = " \
|
||||
|
||||
@@ -28,6 +28,8 @@ SRC_URI = "https://sourceware.org/elfutils/ftp/${PV}/${BP}.tar.bz2 \
|
||||
file://CVE-2025-1372.patch \
|
||||
file://CVE-2025-1371.patch \
|
||||
file://0007-Fix-build-with-gcc-15.patch \
|
||||
file://CVE-2025-1376.patch \
|
||||
file://CVE-2025-1377.patch \
|
||||
"
|
||||
SRC_URI:append:libc-musl = " \
|
||||
file://0003-musl-utils.patch \
|
||||
|
||||
58
meta/recipes-devtools/elfutils/files/CVE-2025-1376.patch
Normal file
58
meta/recipes-devtools/elfutils/files/CVE-2025-1376.patch
Normal file
@@ -0,0 +1,58 @@
|
||||
From b16f441cca0a4841050e3215a9f120a6d8aea918 Mon Sep 17 00:00:00 2001
|
||||
From: Mark Wielaard <mark@klomp.org>
|
||||
Date: Thu, 13 Feb 2025 00:02:32 +0100
|
||||
Subject: [PATCH] libelf: Handle elf_strptr on section without any data
|
||||
|
||||
In the unlikely situation that elf_strptr was called on a section with
|
||||
sh_size already set, but that doesn't have any data yet we could crash
|
||||
trying to verify the string to return.
|
||||
|
||||
This could happen for example when a new section was created with
|
||||
elf_newscn, but no data having been added yet.
|
||||
|
||||
* libelf/elf_strptr.c (elf_strptr): Check strscn->rawdata_base
|
||||
is not NULL.
|
||||
|
||||
https://sourceware.org/bugzilla/show_bug.cgi?id=32672
|
||||
|
||||
Signed-off-by: Mark Wielaard <mark@klomp.org>
|
||||
|
||||
CVE: CVE-2025-1376
|
||||
|
||||
Upstream-Status: Backport [https://sourceware.org/git/?p=elfutils.git;a=commit;h=b16f441cca0a4841050e3215a9f120a6d8aea918]
|
||||
|
||||
Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
|
||||
---
|
||||
libelf/elf_strptr.c | 10 +++++++---
|
||||
1 file changed, 7 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/libelf/elf_strptr.c b/libelf/elf_strptr.c
|
||||
index c5a94f8..7be7f5e 100644
|
||||
--- a/libelf/elf_strptr.c
|
||||
+++ b/libelf/elf_strptr.c
|
||||
@@ -1,5 +1,6 @@
|
||||
/* Return string pointer from string section.
|
||||
Copyright (C) 1998-2002, 2004, 2008, 2009, 2015 Red Hat, Inc.
|
||||
+ Copyright (C) 2025 Mark J. Wielaard <mark@klomp.org>
|
||||
This file is part of elfutils.
|
||||
Contributed by Ulrich Drepper <drepper@redhat.com>, 1998.
|
||||
|
||||
@@ -183,9 +184,12 @@ elf_strptr (Elf *elf, size_t idx, size_t offset)
|
||||
// initialized yet (when data_read is zero). So we cannot just
|
||||
// look at the rawdata.d.d_size.
|
||||
|
||||
- /* Make sure the string is NUL terminated. Start from the end,
|
||||
- which very likely is a NUL char. */
|
||||
- if (likely (validate_str (strscn->rawdata_base, offset, sh_size)))
|
||||
+ /* First check there actually is any data. This could be a new
|
||||
+ section which hasn't had any data set yet. Then make sure
|
||||
+ the string is at a valid offset and NUL terminated. */
|
||||
+ if (unlikely (strscn->rawdata_base == NULL))
|
||||
+ __libelf_seterrno (ELF_E_INVALID_SECTION);
|
||||
+ else if (likely (validate_str (strscn->rawdata_base, offset, sh_size)))
|
||||
result = &strscn->rawdata_base[offset];
|
||||
else
|
||||
__libelf_seterrno (ELF_E_INVALID_INDEX);
|
||||
--
|
||||
2.40.0
|
||||
|
||||
69
meta/recipes-devtools/elfutils/files/CVE-2025-1377.patch
Normal file
69
meta/recipes-devtools/elfutils/files/CVE-2025-1377.patch
Normal file
@@ -0,0 +1,69 @@
|
||||
From fbf1df9ca286de3323ae541973b08449f8d03aba Mon Sep 17 00:00:00 2001
|
||||
From: Mark Wielaard <mark@klomp.org>
|
||||
Date: Thu, 13 Feb 2025 14:59:34 +0100
|
||||
Subject: [PATCH] strip: Verify symbol table is a real symbol table
|
||||
|
||||
We didn't check the symbol table referenced from the relocation table
|
||||
was a real symbol table. This could cause a crash if that section
|
||||
happened to be an SHT_NOBITS section without any data. Fix this by
|
||||
adding an explicit check.
|
||||
|
||||
* src/strip.c (INTERNAL_ERROR_MSG): New macro that takes a
|
||||
message string to display.
|
||||
(INTERNAL_ERROR): Use INTERNAL_ERROR_MSG with elf_errmsg (-1).
|
||||
(remove_debug_relocations): Check the sh_link referenced
|
||||
section is real and isn't a SHT_NOBITS section.
|
||||
|
||||
https://sourceware.org/bugzilla/show_bug.cgi?id=32673
|
||||
|
||||
Signed-off-by: Mark Wielaard <mark@klomp.org>
|
||||
|
||||
CVE: CVE-2025-1377
|
||||
|
||||
Upstream-Status: Backport [https://sourceware.org/git/?p=elfutils.git;a=fbf1df9ca286de3323ae541973b08449f8d03aba]
|
||||
|
||||
Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
|
||||
---
|
||||
src/strip.c | 14 +++++++++++---
|
||||
1 file changed, 11 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/strip.c b/src/strip.c
|
||||
index 6436443..16922e9 100644
|
||||
--- a/src/strip.c
|
||||
+++ b/src/strip.c
|
||||
@@ -126,13 +126,14 @@ static char *tmp_debug_fname = NULL;
|
||||
/* Close debug file descriptor, if opened. And remove temporary debug file. */
|
||||
static void cleanup_debug (void);
|
||||
|
||||
-#define INTERNAL_ERROR(fname) \
|
||||
+#define INTERNAL_ERROR_MSG(fname, msg) \
|
||||
do { \
|
||||
cleanup_debug (); \
|
||||
error_exit (0, _("%s: INTERNAL ERROR %d (%s): %s"), \
|
||||
- fname, __LINE__, PACKAGE_VERSION, elf_errmsg (-1)); \
|
||||
+ fname, __LINE__, PACKAGE_VERSION, msg); \
|
||||
} while (0)
|
||||
|
||||
+#define INTERNAL_ERROR(fname) INTERNAL_ERROR_MSG(fname, elf_errmsg (-1))
|
||||
|
||||
/* Name of the output file. */
|
||||
static const char *output_fname;
|
||||
@@ -631,7 +632,14 @@ remove_debug_relocations (Ebl *ebl, Elf *elf, GElf_Ehdr *ehdr,
|
||||
resolve relocation symbol indexes. */
|
||||
Elf64_Word symt = shdr->sh_link;
|
||||
Elf_Data *symdata, *xndxdata;
|
||||
- Elf_Scn * symscn = elf_getscn (elf, symt);
|
||||
+ Elf_Scn *symscn = elf_getscn (elf, symt);
|
||||
+ GElf_Shdr symshdr_mem;
|
||||
+ GElf_Shdr *symshdr = gelf_getshdr (symscn, &symshdr_mem);
|
||||
+ if (symshdr == NULL)
|
||||
+ INTERNAL_ERROR (fname);
|
||||
+ if (symshdr->sh_type == SHT_NOBITS)
|
||||
+ INTERNAL_ERROR_MSG (fname, "NOBITS section");
|
||||
+
|
||||
symdata = elf_getdata (symscn, NULL);
|
||||
xndxdata = get_xndxdata (elf, symscn);
|
||||
if (symdata == NULL)
|
||||
--
|
||||
2.40.0
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
From cec508013706ef06dbac036905a90cbe075fa03d Mon Sep 17 00:00:00 2001
|
||||
From: Richard Barnes <rbarnes@umn.edu>
|
||||
Date: Wed, 2 Oct 2024 10:35:09 -0700
|
||||
Subject: [PATCH] Match `malloc` signature to its use
|
||||
|
||||
Upstream-Status: Submitted [https://github.com/westes/flex/pull/674]
|
||||
|
||||
Signed-off-by: Martin Jansa <martin.jansa@gmail.com>
|
||||
---
|
||||
lib/malloc.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/lib/malloc.c b/lib/malloc.c
|
||||
index 75e8ef9..701b9b3 100755
|
||||
--- a/lib/malloc.c
|
||||
+++ b/lib/malloc.c
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
- void *malloc ();
|
||||
+ void *malloc (size_t n);
|
||||
|
||||
/* Allocate an N-byte block of memory from the heap.
|
||||
If N is zero, allocate a 1-byte block. */
|
||||
@@ -19,6 +19,7 @@ SRC_URI = "${GITHUB_BASE_URI}/download/v${PV}/flex-${PV}.tar.gz \
|
||||
file://0001-build-AC_USE_SYSTEM_EXTENSIONS-in-configure.ac.patch \
|
||||
file://check-funcs.patch \
|
||||
file://0001-Emit-no-line-directives-if-gen_line_dirs-is-false.patch \
|
||||
file://0001-Match-malloc-signature-to-its-use.patch \
|
||||
"
|
||||
|
||||
SRC_URI[md5sum] = "2882e3179748cc9f9c23ec593d6adc8d"
|
||||
|
||||
@@ -21,6 +21,13 @@ SRC_URI += "\
|
||||
file://CVE-2025-47907-pre.patch \
|
||||
file://CVE-2025-47907.patch \
|
||||
file://CVE-2025-47906.patch \
|
||||
file://CVE-2025-58185.patch \
|
||||
file://CVE-2025-58187.patch \
|
||||
file://CVE-2025-58188.patch \
|
||||
file://CVE-2025-58189.patch \
|
||||
file://CVE-2025-47912.patch \
|
||||
file://CVE-2025-61723.patch \
|
||||
file://CVE-2025-61724.patch \
|
||||
"
|
||||
SRC_URI[main.sha256sum] = "012a7e1f37f362c0918c1dfa3334458ac2da1628c4b9cf4d9ca02db986e17d71"
|
||||
|
||||
|
||||
226
meta/recipes-devtools/go/go/CVE-2025-47912.patch
Normal file
226
meta/recipes-devtools/go/go/CVE-2025-47912.patch
Normal file
@@ -0,0 +1,226 @@
|
||||
From d6d2f7bf76718f1db05461cd912ae5e30d7b77ea Mon Sep 17 00:00:00 2001
|
||||
From: Ethan Lee <ethanalee@google.com>
|
||||
Date: Fri, 29 Aug 2025 17:35:55 +0000
|
||||
Subject: [PATCH] [release-branch.go1.24] net/url: enforce stricter parsing of
|
||||
|
||||
bracketed IPv6 hostnames - Previously, url.Parse did not enforce validation
|
||||
of hostnames within square brackets. - RFC 3986 stipulates that only IPv6
|
||||
hostnames can be embedded within square brackets in a URL. - Now, the
|
||||
parsing logic should strictly enforce that only IPv6 hostnames can be
|
||||
resolved when in square brackets. IPv4, IPv4-mapped addresses and other
|
||||
input will be rejected. - Update url_test to add test cases that cover the
|
||||
above scenarios.
|
||||
|
||||
Thanks to Enze Wang, Jingcheng Yang and Zehui Miao of Tsinghua
|
||||
University for reporting this issue.
|
||||
|
||||
Fixes CVE-2025-47912
|
||||
Fixes #75678
|
||||
Fixes #75712
|
||||
|
||||
Change-Id: Iaa41432bf0ee86de95a39a03adae5729e4deb46c
|
||||
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2680
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2968
|
||||
Reviewed-by: Nicholas Husin <husin@google.com>
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/709838
|
||||
TryBot-Bypass: Michael Pratt <mpratt@google.com>
|
||||
Reviewed-by: Carlos Amedee <carlos@golang.org>
|
||||
Auto-Submit: Michael Pratt <mpratt@google.com>
|
||||
|
||||
CVE: CVE-2025-47912
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/d6d2f7bf76718f1db05461cd912ae5e30d7b77ea]
|
||||
|
||||
Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
|
||||
---
|
||||
src/go/build/deps_test.go | 9 ++++++---
|
||||
src/net/url/url.go | 42 +++++++++++++++++++++++++++++----------
|
||||
src/net/url/url_test.go | 39 ++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 77 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
|
||||
index 7ce8d34..9f2663f 100644
|
||||
--- a/src/go/build/deps_test.go
|
||||
+++ b/src/go/build/deps_test.go
|
||||
@@ -209,7 +209,6 @@ var depsRules = `
|
||||
internal/types/errors,
|
||||
mime/quotedprintable,
|
||||
net/internal/socktest,
|
||||
- net/url,
|
||||
runtime/trace,
|
||||
text/scanner,
|
||||
text/tabwriter;
|
||||
@@ -252,6 +251,12 @@ var depsRules = `
|
||||
FMT
|
||||
< text/template/parse;
|
||||
|
||||
+ internal/bytealg, internal/itoa, math/bits, slices, strconv, unique
|
||||
+ < net/netip;
|
||||
+
|
||||
+ FMT, net/netip
|
||||
+ < net/url;
|
||||
+
|
||||
net/url, text/template/parse
|
||||
< text/template
|
||||
< internal/lazytemplate;
|
||||
@@ -367,8 +372,6 @@ var depsRules = `
|
||||
internal/godebug
|
||||
< internal/intern;
|
||||
|
||||
- internal/bytealg, internal/intern, internal/itoa, math/bits, sort, strconv
|
||||
- < net/netip;
|
||||
|
||||
# net is unavoidable when doing any networking,
|
||||
# so large dependencies must be kept out.
|
||||
diff --git a/src/net/url/url.go b/src/net/url/url.go
|
||||
index f362958..d2ae032 100644
|
||||
--- a/src/net/url/url.go
|
||||
+++ b/src/net/url/url.go
|
||||
@@ -13,6 +13,7 @@ package url
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
+ "net/netip"
|
||||
"path"
|
||||
"sort"
|
||||
"strconv"
|
||||
@@ -621,40 +622,61 @@ func parseAuthority(authority string) (user *Userinfo, host string, err error) {
|
||||
// parseHost parses host as an authority without user
|
||||
// information. That is, as host[:port].
|
||||
func parseHost(host string) (string, error) {
|
||||
- if strings.HasPrefix(host, "[") {
|
||||
+ if openBracketIdx := strings.LastIndex(host, "["); openBracketIdx != -1 {
|
||||
// Parse an IP-Literal in RFC 3986 and RFC 6874.
|
||||
// E.g., "[fe80::1]", "[fe80::1%25en0]", "[fe80::1]:80".
|
||||
- i := strings.LastIndex(host, "]")
|
||||
- if i < 0 {
|
||||
+ closeBracketIdx := strings.LastIndex(host, "]")
|
||||
+ if closeBracketIdx < 0 {
|
||||
return "", errors.New("missing ']' in host")
|
||||
}
|
||||
- colonPort := host[i+1:]
|
||||
+
|
||||
+ colonPort := host[closeBracketIdx+1:]
|
||||
if !validOptionalPort(colonPort) {
|
||||
return "", fmt.Errorf("invalid port %q after host", colonPort)
|
||||
}
|
||||
+ unescapedColonPort, err := unescape(colonPort, encodeHost)
|
||||
+ if err != nil {
|
||||
+ return "", err
|
||||
+ }
|
||||
|
||||
+ hostname := host[openBracketIdx+1 : closeBracketIdx]
|
||||
+ var unescapedHostname string
|
||||
// RFC 6874 defines that %25 (%-encoded percent) introduces
|
||||
// the zone identifier, and the zone identifier can use basically
|
||||
// any %-encoding it likes. That's different from the host, which
|
||||
// can only %-encode non-ASCII bytes.
|
||||
// We do impose some restrictions on the zone, to avoid stupidity
|
||||
// like newlines.
|
||||
- zone := strings.Index(host[:i], "%25")
|
||||
- if zone >= 0 {
|
||||
- host1, err := unescape(host[:zone], encodeHost)
|
||||
+ zoneIdx := strings.Index(hostname, "%25")
|
||||
+ if zoneIdx >= 0 {
|
||||
+ hostPart, err := unescape(hostname[:zoneIdx], encodeHost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
- host2, err := unescape(host[zone:i], encodeZone)
|
||||
+ zonePart, err := unescape(hostname[zoneIdx:], encodeZone)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
- host3, err := unescape(host[i:], encodeHost)
|
||||
+ unescapedHostname = hostPart + zonePart
|
||||
+ } else {
|
||||
+ var err error
|
||||
+ unescapedHostname, err = unescape(hostname, encodeHost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
- return host1 + host2 + host3, nil
|
||||
}
|
||||
+
|
||||
+ // Per RFC 3986, only a host identified by a valid
|
||||
+ // IPv6 address can be enclosed by square brackets.
|
||||
+ // This excludes any IPv4 or IPv4-mapped addresses.
|
||||
+ addr, err := netip.ParseAddr(unescapedHostname)
|
||||
+ if err != nil {
|
||||
+ return "", fmt.Errorf("invalid host: %w", err)
|
||||
+ }
|
||||
+ if addr.Is4() || addr.Is4In6() {
|
||||
+ return "", errors.New("invalid IPv6 host")
|
||||
+ }
|
||||
+ return "[" + unescapedHostname + "]" + unescapedColonPort, nil
|
||||
} else if i := strings.LastIndex(host, ":"); i != -1 {
|
||||
colonPort := host[i:]
|
||||
if !validOptionalPort(colonPort) {
|
||||
diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go
|
||||
index 4aa20bb..fef236e 100644
|
||||
--- a/src/net/url/url_test.go
|
||||
+++ b/src/net/url/url_test.go
|
||||
@@ -383,6 +383,16 @@ var urltests = []URLTest{
|
||||
},
|
||||
"",
|
||||
},
|
||||
+ // valid IPv6 host with port and path
|
||||
+ {
|
||||
+ "https://[2001:db8::1]:8443/test/path",
|
||||
+ &URL{
|
||||
+ Scheme: "https",
|
||||
+ Host: "[2001:db8::1]:8443",
|
||||
+ Path: "/test/path",
|
||||
+ },
|
||||
+ "",
|
||||
+ },
|
||||
// host subcomponent; IPv6 address with zone identifier in RFC 6874
|
||||
{
|
||||
"http://[fe80::1%25en0]/", // alphanum zone identifier
|
||||
@@ -707,6 +717,24 @@ var parseRequestURLTests = []struct {
|
||||
// RFC 6874.
|
||||
{"http://[fe80::1%en0]/", false},
|
||||
{"http://[fe80::1%en0]:8080/", false},
|
||||
+
|
||||
+ // Tests exercising RFC 3986 compliance
|
||||
+ {"https://[1:2:3:4:5:6:7:8]", true}, // full IPv6 address
|
||||
+ {"https://[2001:db8::a:b:c:d]", true}, // compressed IPv6 address
|
||||
+ {"https://[fe80::1%25eth0]", true}, // link-local address with zone ID (interface name)
|
||||
+ {"https://[fe80::abc:def%254]", true}, // link-local address with zone ID (interface index)
|
||||
+ {"https://[2001:db8::1]/path", true}, // compressed IPv6 address with path
|
||||
+ {"https://[fe80::1%25eth0]/path?query=1", true}, // link-local with zone, path, and query
|
||||
+
|
||||
+ {"https://[::ffff:192.0.2.1]", false},
|
||||
+ {"https://[:1] ", false},
|
||||
+ {"https://[1:2:3:4:5:6:7:8:9]", false},
|
||||
+ {"https://[1::1::1]", false},
|
||||
+ {"https://[1:2:3:]", false},
|
||||
+ {"https://[ffff::127.0.0.4000]", false},
|
||||
+ {"https://[0:0::test.com]:80", false},
|
||||
+ {"https://[2001:db8::test.com]", false},
|
||||
+ {"https://[test.com]", false},
|
||||
}
|
||||
|
||||
func TestParseRequestURI(t *testing.T) {
|
||||
@@ -1635,6 +1663,17 @@ func TestParseErrors(t *testing.T) {
|
||||
{"cache_object:foo", true},
|
||||
{"cache_object:foo/bar", true},
|
||||
{"cache_object/:foo/bar", false},
|
||||
+
|
||||
+ {"http://[192.168.0.1]/", true}, // IPv4 in brackets
|
||||
+ {"http://[192.168.0.1]:8080/", true}, // IPv4 in brackets with port
|
||||
+ {"http://[::ffff:192.168.0.1]/", true}, // IPv4-mapped IPv6 in brackets
|
||||
+ {"http://[::ffff:192.168.0.1]:8080/", true}, // IPv4-mapped IPv6 in brackets with port
|
||||
+ {"http://[::ffff:c0a8:1]/", true}, // IPv4-mapped IPv6 in brackets (hex)
|
||||
+ {"http://[not-an-ip]/", true}, // invalid IP string in brackets
|
||||
+ {"http://[fe80::1%foo]/", true}, // invalid zone format in brackets
|
||||
+ {"http://[fe80::1", true}, // missing closing bracket
|
||||
+ {"http://fe80::1]/", true}, // missing opening bracket
|
||||
+ {"http://[test.com]/", true}, // domain name in brackets
|
||||
}
|
||||
for _, tt := range tests {
|
||||
u, err := Parse(tt.in)
|
||||
--
|
||||
2.40.0
|
||||
142
meta/recipes-devtools/go/go/CVE-2025-58185.patch
Normal file
142
meta/recipes-devtools/go/go/CVE-2025-58185.patch
Normal file
@@ -0,0 +1,142 @@
|
||||
From 5c3d61c886f7ecfce9a6d6d3c97e6d5a8afb17d1 Mon Sep 17 00:00:00 2001
|
||||
From: Nicholas Husin <husin@google.com>
|
||||
Date: Wed, 3 Sep 2025 09:30:56 -0400
|
||||
Subject: [PATCH] [release-branch.go1.24] encoding/asn1: prevent memory
|
||||
exhaustion when parsing using internal/saferio
|
||||
|
||||
Within parseSequenceOf,
|
||||
reflect.MakeSlice is being used to pre-allocate a slice that is needed in
|
||||
order to fully validate the given DER payload. The size of the slice
|
||||
allocated are also multiple times larger than the input DER:
|
||||
|
||||
- When using asn1.Unmarshal directly, the allocated slice is ~28x
|
||||
larger.
|
||||
- When passing in DER using x509.ParseCertificateRequest, the allocated
|
||||
slice is ~48x larger.
|
||||
- When passing in DER using ocsp.ParseResponse, the allocated slice is
|
||||
~137x larger.
|
||||
|
||||
As a result, a malicious actor can craft a big empty DER payload,
|
||||
resulting in an unnecessary large allocation of memories. This can be a
|
||||
way to cause memory exhaustion.
|
||||
|
||||
To prevent this, we now use SliceCapWithSize within internal/saferio to
|
||||
enforce a memory allocation cap.
|
||||
|
||||
Thanks to Jakub Ciolek for reporting this issue.
|
||||
|
||||
For #75671
|
||||
Fixes #75704
|
||||
Fixes CVE-2025-58185
|
||||
|
||||
Change-Id: Id50e76187eda43f594be75e516b9ca1d2ae6f428
|
||||
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2700
|
||||
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2984
|
||||
Reviewed-by: Nicholas Husin <husin@google.com>
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/709841
|
||||
Reviewed-by: Carlos Amedee <carlos@golang.org>
|
||||
Auto-Submit: Michael Pratt <mpratt@google.com>
|
||||
TryBot-Bypass: Michael Pratt <mpratt@google.com>
|
||||
|
||||
CVE: CVE-2025-58185
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/5c3d61c886f7ecfce9a6d6d3c97e6d5a8afb17d1]
|
||||
|
||||
Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
|
||||
---
|
||||
src/encoding/asn1/asn1.go | 10 ++++++++-
|
||||
src/encoding/asn1/asn1_test.go | 38 ++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 47 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go
|
||||
index 781ab87..16c7138 100644
|
||||
--- a/src/encoding/asn1/asn1.go
|
||||
+++ b/src/encoding/asn1/asn1.go
|
||||
@@ -22,6 +22,7 @@ package asn1
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
+ "internal/saferio"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
@@ -643,10 +644,17 @@ func parseSequenceOf(bytes []byte, sliceType reflect.Type, elemType reflect.Type
|
||||
offset += t.length
|
||||
numElements++
|
||||
}
|
||||
- ret = reflect.MakeSlice(sliceType, numElements, numElements)
|
||||
+ elemSize := uint64(elemType.Size())
|
||||
+ safeCap := saferio.SliceCapWithSize(elemSize, uint64(numElements))
|
||||
+ if safeCap < 0 {
|
||||
+ err = SyntaxError{fmt.Sprintf("%s slice too big: %d elements of %d bytes", elemType.Kind(), numElements, elemSize)}
|
||||
+ return
|
||||
+ }
|
||||
+ ret = reflect.MakeSlice(sliceType, 0, safeCap)
|
||||
params := fieldParameters{}
|
||||
offset := 0
|
||||
for i := 0; i < numElements; i++ {
|
||||
+ ret = reflect.Append(ret, reflect.Zero(elemType))
|
||||
offset, err = parseField(ret.Index(i), bytes, offset, params)
|
||||
if err != nil {
|
||||
return
|
||||
diff --git a/src/encoding/asn1/asn1_test.go b/src/encoding/asn1/asn1_test.go
|
||||
index 9a605e2..249d4e4 100644
|
||||
--- a/src/encoding/asn1/asn1_test.go
|
||||
+++ b/src/encoding/asn1/asn1_test.go
|
||||
@@ -7,10 +7,12 @@ package asn1
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
+ "errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"reflect"
|
||||
+ "runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -1175,3 +1177,39 @@ func BenchmarkObjectIdentifierString(b *testing.B) {
|
||||
_ = oidPublicKeyRSA.String()
|
||||
}
|
||||
}
|
||||
+
|
||||
+func TestParsingMemoryConsumption(t *testing.T) {
|
||||
+ // Craft a syntatically valid, but empty, ~10 MB DER bomb. A successful
|
||||
+ // unmarshal of this bomb should yield ~280 MB. However, the parsing should
|
||||
+ // fail due to the empty content; and, in such cases, we want to make sure
|
||||
+ // that we do not unnecessarily allocate memories.
|
||||
+ derBomb := make([]byte, 10_000_000)
|
||||
+ for i := range derBomb {
|
||||
+ derBomb[i] = 0x30
|
||||
+ }
|
||||
+ derBomb = append([]byte{0x30, 0x83, 0x98, 0x96, 0x80}, derBomb...)
|
||||
+
|
||||
+ var m runtime.MemStats
|
||||
+ runtime.GC()
|
||||
+ runtime.ReadMemStats(&m)
|
||||
+ memBefore := m.TotalAlloc
|
||||
+
|
||||
+ var out []struct {
|
||||
+ Id []int
|
||||
+ Critical bool `asn1:"optional"`
|
||||
+ Value []byte
|
||||
+ }
|
||||
+ _, err := Unmarshal(derBomb, &out)
|
||||
+ if !errors.As(err, &SyntaxError{}) {
|
||||
+ t.Fatalf("Incorrect error result: want (%v), but got (%v) instead", &SyntaxError{}, err)
|
||||
+ }
|
||||
+
|
||||
+ runtime.ReadMemStats(&m)
|
||||
+ memDiff := m.TotalAlloc - memBefore
|
||||
+
|
||||
+ // Ensure that the memory allocated does not exceed 10<<21 (~20 MB) when
|
||||
+ // the parsing fails.
|
||||
+ if memDiff > 10<<21 {
|
||||
+ t.Errorf("Too much memory allocated while parsing DER: %v MiB", memDiff/1024/1024)
|
||||
+ }
|
||||
+}
|
||||
--
|
||||
2.40.0
|
||||
349
meta/recipes-devtools/go/go/CVE-2025-58187.patch
Normal file
349
meta/recipes-devtools/go/go/CVE-2025-58187.patch
Normal file
@@ -0,0 +1,349 @@
|
||||
From f334417e71f8b078ad64035bddb6df7f8910da6c Mon Sep 17 00:00:00 2001
|
||||
From: Neal Patel <nealpatel@google.com>
|
||||
Date: Mon, 15 Sep 2025 16:31:22 -0400
|
||||
Subject: [PATCH] [release-branch.go1.24] crypto/x509: improve domain name
|
||||
verification
|
||||
|
||||
Don't use domainToReverseLabels to check if domain names are
|
||||
valid, since it is not particularly performant, and can contribute to DoS
|
||||
vectors. Instead just iterate over the name and enforce the properties we
|
||||
care about.
|
||||
|
||||
This also enforces that DNS names, both in SANs and name constraints,
|
||||
are valid. We previously allowed invalid SANs, because some
|
||||
intermediates had these weird names (see #23995), but there are
|
||||
currently no trusted intermediates that have this property, and since we
|
||||
target the web PKI, supporting this particular case is not a high
|
||||
priority.
|
||||
|
||||
Thank you to Jakub Ciolek for reporting this issue.
|
||||
|
||||
Fixes CVE-2025-58187
|
||||
For #75681
|
||||
Fixes #75714
|
||||
|
||||
Change-Id: I6ebce847dcbe5fc63ef2f9a74f53f11c4c56d3d1
|
||||
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2820
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2982
|
||||
Reviewed-by: Nicholas Husin <husin@google.com>
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/709839
|
||||
Auto-Submit: Michael Pratt <mpratt@google.com>
|
||||
Reviewed-by: Carlos Amedee <carlos@golang.org>
|
||||
TryBot-Bypass: Michael Pratt <mpratt@google.com>
|
||||
|
||||
CVE: CVE-2025-58187
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/f334417e71f8b078ad64035bddb6df7f8910da6c]
|
||||
|
||||
Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
|
||||
---
|
||||
src/crypto/x509/name_constraints_test.go | 66 ++------------------
|
||||
src/crypto/x509/parser.go | 77 ++++++++++++++----------
|
||||
src/crypto/x509/parser_test.go | 43 +++++++++++++
|
||||
src/crypto/x509/verify.go | 1 +
|
||||
4 files changed, 95 insertions(+), 92 deletions(-)
|
||||
|
||||
diff --git a/src/crypto/x509/name_constraints_test.go b/src/crypto/x509/name_constraints_test.go
|
||||
index 78263fc..9aaa6d7 100644
|
||||
--- a/src/crypto/x509/name_constraints_test.go
|
||||
+++ b/src/crypto/x509/name_constraints_test.go
|
||||
@@ -1456,63 +1456,7 @@ var nameConstraintsTests = []nameConstraintsTest{
|
||||
expectedError: "incompatible key usage",
|
||||
},
|
||||
|
||||
- // An invalid DNS SAN should be detected only at validation time so
|
||||
- // that we can process CA certificates in the wild that have invalid SANs.
|
||||
- // See https://github.com/golang/go/issues/23995
|
||||
-
|
||||
- // #77: an invalid DNS or mail SAN will not be detected if name constraint
|
||||
- // checking is not triggered.
|
||||
- {
|
||||
- roots: make([]constraintsSpec, 1),
|
||||
- intermediates: [][]constraintsSpec{
|
||||
- {
|
||||
- {},
|
||||
- },
|
||||
- },
|
||||
- leaf: leafSpec{
|
||||
- sans: []string{"dns:this is invalid", "email:this @ is invalid"},
|
||||
- },
|
||||
- },
|
||||
-
|
||||
- // #78: an invalid DNS SAN will be detected if any name constraint checking
|
||||
- // is triggered.
|
||||
- {
|
||||
- roots: []constraintsSpec{
|
||||
- {
|
||||
- bad: []string{"uri:"},
|
||||
- },
|
||||
- },
|
||||
- intermediates: [][]constraintsSpec{
|
||||
- {
|
||||
- {},
|
||||
- },
|
||||
- },
|
||||
- leaf: leafSpec{
|
||||
- sans: []string{"dns:this is invalid"},
|
||||
- },
|
||||
- expectedError: "cannot parse dnsName",
|
||||
- },
|
||||
-
|
||||
- // #79: an invalid email SAN will be detected if any name constraint
|
||||
- // checking is triggered.
|
||||
- {
|
||||
- roots: []constraintsSpec{
|
||||
- {
|
||||
- bad: []string{"uri:"},
|
||||
- },
|
||||
- },
|
||||
- intermediates: [][]constraintsSpec{
|
||||
- {
|
||||
- {},
|
||||
- },
|
||||
- },
|
||||
- leaf: leafSpec{
|
||||
- sans: []string{"email:this @ is invalid"},
|
||||
- },
|
||||
- expectedError: "cannot parse rfc822Name",
|
||||
- },
|
||||
-
|
||||
- // #80: if several EKUs are requested, satisfying any of them is sufficient.
|
||||
+ // #77: if several EKUs are requested, satisfying any of them is sufficient.
|
||||
{
|
||||
roots: make([]constraintsSpec, 1),
|
||||
intermediates: [][]constraintsSpec{
|
||||
@@ -1527,7 +1471,7 @@ var nameConstraintsTests = []nameConstraintsTest{
|
||||
requestedEKUs: []ExtKeyUsage{ExtKeyUsageClientAuth, ExtKeyUsageEmailProtection},
|
||||
},
|
||||
|
||||
- // #81: EKUs that are not asserted in VerifyOpts are not required to be
|
||||
+ // #78: EKUs that are not asserted in VerifyOpts are not required to be
|
||||
// nested.
|
||||
{
|
||||
roots: make([]constraintsSpec, 1),
|
||||
@@ -1546,7 +1490,7 @@ var nameConstraintsTests = []nameConstraintsTest{
|
||||
},
|
||||
},
|
||||
|
||||
- // #82: a certificate without SANs and CN is accepted in a constrained chain.
|
||||
+ // #79: a certificate without SANs and CN is accepted in a constrained chain.
|
||||
{
|
||||
roots: []constraintsSpec{
|
||||
{
|
||||
@@ -1563,7 +1507,7 @@ var nameConstraintsTests = []nameConstraintsTest{
|
||||
},
|
||||
},
|
||||
|
||||
- // #83: a certificate without SANs and with a CN that does not parse as a
|
||||
+ // #80: a certificate without SANs and with a CN that does not parse as a
|
||||
// hostname is accepted in a constrained chain.
|
||||
{
|
||||
roots: []constraintsSpec{
|
||||
@@ -1582,7 +1526,7 @@ var nameConstraintsTests = []nameConstraintsTest{
|
||||
},
|
||||
},
|
||||
|
||||
- // #84: a certificate with SANs and CN is accepted in a constrained chain.
|
||||
+ // #81: a certificate with SANs and CN is accepted in a constrained chain.
|
||||
{
|
||||
roots: []constraintsSpec{
|
||||
{
|
||||
diff --git a/src/crypto/x509/parser.go b/src/crypto/x509/parser.go
|
||||
index 812b0d2..9a3bcd6 100644
|
||||
--- a/src/crypto/x509/parser.go
|
||||
+++ b/src/crypto/x509/parser.go
|
||||
@@ -378,10 +378,14 @@ func parseSANExtension(der cryptobyte.String) (dnsNames, emailAddresses []string
|
||||
if err := isIA5String(email); err != nil {
|
||||
return errors.New("x509: SAN rfc822Name is malformed")
|
||||
}
|
||||
+ parsed, ok := parseRFC2821Mailbox(email)
|
||||
+ if !ok || (ok && !domainNameValid(parsed.domain, false)) {
|
||||
+ return errors.New("x509: SAN rfc822Name is malformed")
|
||||
+ }
|
||||
emailAddresses = append(emailAddresses, email)
|
||||
case nameTypeDNS:
|
||||
name := string(data)
|
||||
- if err := isIA5String(name); err != nil {
|
||||
+ if err := isIA5String(name); err != nil || (err == nil && !domainNameValid(name, false)) {
|
||||
return errors.New("x509: SAN dNSName is malformed")
|
||||
}
|
||||
dnsNames = append(dnsNames, string(name))
|
||||
@@ -391,14 +395,9 @@ func parseSANExtension(der cryptobyte.String) (dnsNames, emailAddresses []string
|
||||
return errors.New("x509: SAN uniformResourceIdentifier is malformed")
|
||||
}
|
||||
uri, err := url.Parse(uriStr)
|
||||
- if err != nil {
|
||||
+ if err != nil || (err == nil && uri.Host != "" && !domainNameValid(uri.Host, false)) {
|
||||
return fmt.Errorf("x509: cannot parse URI %q: %s", uriStr, err)
|
||||
}
|
||||
- if len(uri.Host) > 0 {
|
||||
- if _, ok := domainToReverseLabels(uri.Host); !ok {
|
||||
- return fmt.Errorf("x509: cannot parse URI %q: invalid domain", uriStr)
|
||||
- }
|
||||
- }
|
||||
uris = append(uris, uri)
|
||||
case nameTypeIP:
|
||||
switch len(data) {
|
||||
@@ -538,15 +537,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle
|
||||
return nil, nil, nil, nil, errors.New("x509: invalid constraint value: " + err.Error())
|
||||
}
|
||||
|
||||
- trimmedDomain := domain
|
||||
- if len(trimmedDomain) > 0 && trimmedDomain[0] == '.' {
|
||||
- // constraints can have a leading
|
||||
- // period to exclude the domain
|
||||
- // itself, but that's not valid in a
|
||||
- // normal domain name.
|
||||
- trimmedDomain = trimmedDomain[1:]
|
||||
- }
|
||||
- if _, ok := domainToReverseLabels(trimmedDomain); !ok {
|
||||
+ if !domainNameValid(domain, true) {
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse dnsName constraint %q", domain)
|
||||
}
|
||||
dnsNames = append(dnsNames, domain)
|
||||
@@ -587,12 +578,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)
|
||||
}
|
||||
} else {
|
||||
- // Otherwise it's a domain name.
|
||||
- domain := constraint
|
||||
- if len(domain) > 0 && domain[0] == '.' {
|
||||
- domain = domain[1:]
|
||||
- }
|
||||
- if _, ok := domainToReverseLabels(domain); !ok {
|
||||
+ if !domainNameValid(constraint, true) {
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse rfc822Name constraint %q", constraint)
|
||||
}
|
||||
}
|
||||
@@ -608,15 +594,7 @@ func parseNameConstraintsExtension(out *Certificate, e pkix.Extension) (unhandle
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q: cannot be IP address", domain)
|
||||
}
|
||||
|
||||
- trimmedDomain := domain
|
||||
- if len(trimmedDomain) > 0 && trimmedDomain[0] == '.' {
|
||||
- // constraints can have a leading
|
||||
- // period to exclude the domain itself,
|
||||
- // but that's not valid in a normal
|
||||
- // domain name.
|
||||
- trimmedDomain = trimmedDomain[1:]
|
||||
- }
|
||||
- if _, ok := domainToReverseLabels(trimmedDomain); !ok {
|
||||
+ if !domainNameValid(domain, true) {
|
||||
return nil, nil, nil, nil, fmt.Errorf("x509: failed to parse URI constraint %q", domain)
|
||||
}
|
||||
uriDomains = append(uriDomains, domain)
|
||||
@@ -1197,3 +1175,40 @@ func ParseRevocationList(der []byte) (*RevocationList, error) {
|
||||
|
||||
return rl, nil
|
||||
}
|
||||
+
|
||||
+// domainNameValid does minimal domain name validity checking. In particular it
|
||||
+// enforces the following properties:
|
||||
+// - names cannot have the trailing period
|
||||
+// - names can only have a leading period if constraint is true
|
||||
+// - names must be <= 253 characters
|
||||
+// - names cannot have empty labels
|
||||
+// - names cannot labels that are longer than 63 characters
|
||||
+//
|
||||
+// Note that this does not enforce the LDH requirements for domain names.
|
||||
+func domainNameValid(s string, constraint bool) bool {
|
||||
+ if len(s) == 0 && constraint {
|
||||
+ return true
|
||||
+ }
|
||||
+ if len(s) == 0 || (!constraint && s[0] == '.') || s[len(s)-1] == '.' || len(s) > 253 {
|
||||
+ return false
|
||||
+ }
|
||||
+ lastDot := -1
|
||||
+ if constraint && s[0] == '.' {
|
||||
+ s = s[1:]
|
||||
+ }
|
||||
+
|
||||
+ for i := 0; i <= len(s); i++ {
|
||||
+ if i == len(s) || s[i] == '.' {
|
||||
+ labelLen := i
|
||||
+ if lastDot >= 0 {
|
||||
+ labelLen -= lastDot + 1
|
||||
+ }
|
||||
+ if labelLen == 0 || labelLen > 63 {
|
||||
+ return false
|
||||
+ }
|
||||
+ lastDot = i
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return true
|
||||
+}
|
||||
diff --git a/src/crypto/x509/parser_test.go b/src/crypto/x509/parser_test.go
|
||||
index b31f9cd..a6cdfb8 100644
|
||||
--- a/src/crypto/x509/parser_test.go
|
||||
+++ b/src/crypto/x509/parser_test.go
|
||||
@@ -6,6 +6,7 @@ package x509
|
||||
|
||||
import (
|
||||
"encoding/asn1"
|
||||
+ "strings"
|
||||
"testing"
|
||||
|
||||
cryptobyte_asn1 "golang.org/x/crypto/cryptobyte/asn1"
|
||||
@@ -101,3 +102,45 @@ func TestParseASN1String(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
+
|
||||
+func TestDomainNameValid(t *testing.T) {
|
||||
+ for _, tc := range []struct {
|
||||
+ name string
|
||||
+ dnsName string
|
||||
+ constraint bool
|
||||
+ valid bool
|
||||
+ }{
|
||||
+ {"empty name, name", "", false, false},
|
||||
+ {"empty name, constraint", "", true, true},
|
||||
+ {"empty label, name", "a..a", false, false},
|
||||
+ {"empty label, constraint", "a..a", true, false},
|
||||
+ {"period, name", ".", false, false},
|
||||
+ {"period, constraint", ".", true, false}, // TODO(roland): not entirely clear if this is a valid constraint (require at least one label?)
|
||||
+ {"valid, name", "a.b.c", false, true},
|
||||
+ {"valid, constraint", "a.b.c", true, true},
|
||||
+ {"leading period, name", ".a.b.c", false, false},
|
||||
+ {"leading period, constraint", ".a.b.c", true, true},
|
||||
+ {"trailing period, name", "a.", false, false},
|
||||
+ {"trailing period, constraint", "a.", true, false},
|
||||
+ {"bare label, name", "a", false, true},
|
||||
+ {"bare label, constraint", "a", true, true},
|
||||
+ {"254 char label, name", strings.Repeat("a.a", 84) + "aaa", false, false},
|
||||
+ {"254 char label, constraint", strings.Repeat("a.a", 84) + "aaa", true, false},
|
||||
+ {"253 char label, name", strings.Repeat("a.a", 84) + "aa", false, false},
|
||||
+ {"253 char label, constraint", strings.Repeat("a.a", 84) + "aa", true, false},
|
||||
+ {"64 char single label, name", strings.Repeat("a", 64), false, false},
|
||||
+ {"64 char single label, constraint", strings.Repeat("a", 64), true, false},
|
||||
+ {"63 char single label, name", strings.Repeat("a", 63), false, true},
|
||||
+ {"63 char single label, constraint", strings.Repeat("a", 63), true, true},
|
||||
+ {"64 char label, name", "a." + strings.Repeat("a", 64), false, false},
|
||||
+ {"64 char label, constraint", "a." + strings.Repeat("a", 64), true, false},
|
||||
+ {"63 char label, name", "a." + strings.Repeat("a", 63), false, true},
|
||||
+ {"63 char label, constraint", "a." + strings.Repeat("a", 63), true, true},
|
||||
+ } {
|
||||
+ t.Run(tc.name, func(t *testing.T) {
|
||||
+ if tc.valid != domainNameValid(tc.dnsName, tc.constraint) {
|
||||
+ t.Errorf("domainNameValid(%q, %t) = %v; want %v", tc.dnsName, tc.constraint, !tc.valid, tc.valid)
|
||||
+ }
|
||||
+ })
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go
|
||||
index 2d2a271..4502d4c 100644
|
||||
--- a/src/crypto/x509/verify.go
|
||||
+++ b/src/crypto/x509/verify.go
|
||||
@@ -360,6 +360,7 @@ func parseRFC2821Mailbox(in string) (mailbox rfc2821Mailbox, ok bool) {
|
||||
// domainToReverseLabels converts a textual domain name like foo.example.com to
|
||||
// the list of labels in reverse order, e.g. ["com", "example", "foo"].
|
||||
func domainToReverseLabels(domain string) (reverseLabels []string, ok bool) {
|
||||
+ reverseLabels = make([]string, 0, strings.Count(domain, ".")+1)
|
||||
for len(domain) > 0 {
|
||||
if i := strings.LastIndexByte(domain, '.'); i == -1 {
|
||||
reverseLabels = append(reverseLabels, domain)
|
||||
--
|
||||
2.40.0
|
||||
194
meta/recipes-devtools/go/go/CVE-2025-58188.patch
Normal file
194
meta/recipes-devtools/go/go/CVE-2025-58188.patch
Normal file
@@ -0,0 +1,194 @@
|
||||
From f9f198ab05e3282cbf6b13251d47d9141981e401 Mon Sep 17 00:00:00 2001
|
||||
From: Neal Patel <nealpatel@google.com>
|
||||
Date: Thu, 11 Sep 2025 16:27:04 -0400
|
||||
Subject: [PATCH] [release-branch.go1.24] crypto/x509: mitigate DoS vector when
|
||||
intermediate certificate contains DSA public key An attacker could craft an
|
||||
intermediate X.509 certificate containing a DSA public key and can crash a
|
||||
remote host with an unauthenticated call to any endpoint that verifies the
|
||||
certificate chain.
|
||||
|
||||
Thank you to Jakub Ciolek for reporting this issue.
|
||||
|
||||
Fixes CVE-2025-58188
|
||||
For #75675
|
||||
Fixes #75702
|
||||
|
||||
Change-Id: I2ecbb87b9b8268dbc55c8795891e596ab60f0088
|
||||
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2780
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2964
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/709836
|
||||
TryBot-Bypass: Michael Pratt <mpratt@google.com>
|
||||
Reviewed-by: Carlos Amedee <carlos@golang.org>
|
||||
Auto-Submit: Michael Pratt <mpratt@google.com>
|
||||
|
||||
CVE: CVE-2025-58188
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/f9f198ab05e3282cbf6b13251d47d9141981e401]
|
||||
|
||||
Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
|
||||
---
|
||||
src/crypto/x509/verify.go | 5 +-
|
||||
src/crypto/x509/verify_test.go | 126 +++++++++++++++++++++++++++++++++
|
||||
2 files changed, 130 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go
|
||||
index 4502d4c..14cd23f 100644
|
||||
--- a/src/crypto/x509/verify.go
|
||||
+++ b/src/crypto/x509/verify.go
|
||||
@@ -868,7 +868,10 @@ func alreadyInChain(candidate *Certificate, chain []*Certificate) bool {
|
||||
if !bytes.Equal(candidate.RawSubject, cert.RawSubject) {
|
||||
continue
|
||||
}
|
||||
- if !candidate.PublicKey.(pubKeyEqual).Equal(cert.PublicKey) {
|
||||
+ // We enforce the canonical encoding of SPKI (by only allowing the
|
||||
+ // correct AI paremeter encodings in parseCertificate), so it's safe to
|
||||
+ // directly compare the raw bytes.
|
||||
+ if !bytes.Equal(candidate.RawSubjectPublicKeyInfo, cert.RawSubjectPublicKeyInfo) {
|
||||
continue
|
||||
}
|
||||
var certSAN *pkix.Extension
|
||||
diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go
|
||||
index 8a7a5f6..4a7d8da 100644
|
||||
--- a/src/crypto/x509/verify_test.go
|
||||
+++ b/src/crypto/x509/verify_test.go
|
||||
@@ -6,6 +6,7 @@ package x509
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
+ "crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
@@ -2811,3 +2812,128 @@ func TestVerifyNilPubKey(t *testing.T) {
|
||||
t.Fatalf("buildChains returned unexpected error, got: %v, want %v", err, UnknownAuthorityError{})
|
||||
}
|
||||
}
|
||||
+func TestCertificateChainSignedByECDSA(t *testing.T) {
|
||||
+ caKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ root := &Certificate{
|
||||
+ SerialNumber: big.NewInt(1),
|
||||
+ Subject: pkix.Name{CommonName: "X"},
|
||||
+ NotBefore: time.Now().Add(-time.Hour),
|
||||
+ NotAfter: time.Now().Add(365 * 24 * time.Hour),
|
||||
+ IsCA: true,
|
||||
+ KeyUsage: KeyUsageCertSign | KeyUsageCRLSign,
|
||||
+ BasicConstraintsValid: true,
|
||||
+ }
|
||||
+ caDER, err := CreateCertificate(rand.Reader, root, root, &caKey.PublicKey, caKey)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ root, err = ParseCertificate(caDER)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+
|
||||
+ leafKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
+ leaf := &Certificate{
|
||||
+ SerialNumber: big.NewInt(42),
|
||||
+ Subject: pkix.Name{CommonName: "leaf"},
|
||||
+ NotBefore: time.Now().Add(-10 * time.Minute),
|
||||
+ NotAfter: time.Now().Add(24 * time.Hour),
|
||||
+ KeyUsage: KeyUsageDigitalSignature,
|
||||
+ ExtKeyUsage: []ExtKeyUsage{ExtKeyUsageServerAuth},
|
||||
+ BasicConstraintsValid: true,
|
||||
+ }
|
||||
+ leafDER, err := CreateCertificate(rand.Reader, leaf, root, &leafKey.PublicKey, caKey)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ leaf, err = ParseCertificate(leafDER)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+
|
||||
+ inter, err := ParseCertificate(dsaSelfSignedCNX(t))
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+
|
||||
+ inters := NewCertPool()
|
||||
+ inters.AddCert(root)
|
||||
+ inters.AddCert(inter)
|
||||
+
|
||||
+ wantErr := "certificate signed by unknown authority"
|
||||
+ _, err = leaf.Verify(VerifyOptions{Intermediates: inters, Roots: NewCertPool()})
|
||||
+ if !strings.Contains(err.Error(), wantErr) {
|
||||
+ t.Errorf("got %v, want %q", err, wantErr)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+// dsaSelfSignedCNX produces DER-encoded
|
||||
+// certificate with the properties:
|
||||
+//
|
||||
+// Subject=Issuer=CN=X
|
||||
+// DSA SPKI
|
||||
+// Matching inner/outer signature OIDs
|
||||
+// Dummy ECDSA signature
|
||||
+func dsaSelfSignedCNX(t *testing.T) []byte {
|
||||
+ t.Helper()
|
||||
+ var params dsa.Parameters
|
||||
+ if err := dsa.GenerateParameters(¶ms, rand.Reader, dsa.L1024N160); err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+
|
||||
+ var dsaPriv dsa.PrivateKey
|
||||
+ dsaPriv.Parameters = params
|
||||
+ if err := dsa.GenerateKey(&dsaPriv, rand.Reader); err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ dsaPub := &dsaPriv.PublicKey
|
||||
+
|
||||
+ type dsaParams struct{ P, Q, G *big.Int }
|
||||
+ paramDER, err := asn1.Marshal(dsaParams{dsaPub.P, dsaPub.Q, dsaPub.G})
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ yDER, err := asn1.Marshal(dsaPub.Y)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+
|
||||
+ spki := publicKeyInfo{
|
||||
+ Algorithm: pkix.AlgorithmIdentifier{
|
||||
+ Algorithm: oidPublicKeyDSA,
|
||||
+ Parameters: asn1.RawValue{FullBytes: paramDER},
|
||||
+ },
|
||||
+ PublicKey: asn1.BitString{Bytes: yDER, BitLength: 8 * len(yDER)},
|
||||
+ }
|
||||
+
|
||||
+ rdn := pkix.Name{CommonName: "X"}.ToRDNSequence()
|
||||
+ b, err := asn1.Marshal(rdn)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ rawName := asn1.RawValue{FullBytes: b}
|
||||
+
|
||||
+ algoIdent := pkix.AlgorithmIdentifier{Algorithm: oidSignatureDSAWithSHA256}
|
||||
+ tbs := tbsCertificate{
|
||||
+ Version: 0,
|
||||
+ SerialNumber: big.NewInt(1002),
|
||||
+ SignatureAlgorithm: algoIdent,
|
||||
+ Issuer: rawName,
|
||||
+ Validity: validity{NotBefore: time.Now().Add(-time.Hour), NotAfter: time.Now().Add(24 * time.Hour)},
|
||||
+ Subject: rawName,
|
||||
+ PublicKey: spki,
|
||||
+ }
|
||||
+ c := certificate{
|
||||
+ TBSCertificate: tbs,
|
||||
+ SignatureAlgorithm: algoIdent,
|
||||
+ SignatureValue: asn1.BitString{Bytes: []byte{0}, BitLength: 8},
|
||||
+ }
|
||||
+ dsaDER, err := asn1.Marshal(c)
|
||||
+ if err != nil {
|
||||
+ t.Fatal(err)
|
||||
+ }
|
||||
+ return dsaDER
|
||||
+}
|
||||
--
|
||||
2.40.0
|
||||
50
meta/recipes-devtools/go/go/CVE-2025-58189.patch
Normal file
50
meta/recipes-devtools/go/go/CVE-2025-58189.patch
Normal file
@@ -0,0 +1,50 @@
|
||||
From 2e1e356e33b9c792a9643749a7626a1789197bb9 Mon Sep 17 00:00:00 2001
|
||||
From: Roland Shoemaker <roland@golang.org>
|
||||
Date: Mon, 29 Sep 2025 10:11:56 -0700
|
||||
Subject: [PATCH] crypto/tls: quote protocols in ALPN error message
|
||||
|
||||
Quote the protocols sent by the client when returning the ALPN
|
||||
negotiation error message.
|
||||
|
||||
Fixes CVE-2025-58189
|
||||
Updates #75652
|
||||
Fixes #75660
|
||||
|
||||
Change-Id: Ie7b3a1ed0b6efcc1705b71f0f1e8417126661330
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/707776
|
||||
Auto-Submit: Roland Shoemaker <roland@golang.org>
|
||||
Reviewed-by: Neal Patel <nealpatel@google.com>
|
||||
Reviewed-by: Nicholas Husin <nsh@golang.org>
|
||||
Auto-Submit: Nicholas Husin <nsh@golang.org>
|
||||
Reviewed-by: Nicholas Husin <husin@google.com>
|
||||
TryBot-Bypass: Roland Shoemaker <roland@golang.org>
|
||||
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
|
||||
(cherry picked from commit 4e9006a716533fe1c7ee08df02dfc73078f7dc19)
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/708096
|
||||
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
|
||||
Reviewed-by: Carlos Amedee <carlos@golang.org>
|
||||
|
||||
CVE: CVE-2025-58189
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/2e1e356e33b9c792a9643749a7626a1789197bb9]
|
||||
|
||||
Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
|
||||
---
|
||||
src/crypto/tls/handshake_server.go | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go
|
||||
index 4e84aa9..17b6891 100644
|
||||
--- a/src/crypto/tls/handshake_server.go
|
||||
+++ b/src/crypto/tls/handshake_server.go
|
||||
@@ -312,7 +312,7 @@ func negotiateALPN(serverProtos, clientProtos []string, quic bool) (string, erro
|
||||
if http11fallback {
|
||||
return "", nil
|
||||
}
|
||||
- return "", fmt.Errorf("tls: client requested unsupported application protocols (%s)", clientProtos)
|
||||
+ return "", fmt.Errorf("tls: client requested unsupported application protocols (%q)", clientProtos)
|
||||
}
|
||||
|
||||
// supportsECDHE returns whether ECDHE key exchanges can be used with this
|
||||
--
|
||||
2.40.0
|
||||
223
meta/recipes-devtools/go/go/CVE-2025-61723.patch
Normal file
223
meta/recipes-devtools/go/go/CVE-2025-61723.patch
Normal file
@@ -0,0 +1,223 @@
|
||||
From 74d4d836b91318a8764b94bc2b4b66ff599eb5f2 Mon Sep 17 00:00:00 2001
|
||||
From: Roland Shoemaker <bracewell@google.com>
|
||||
Date: Tue, 30 Sep 2025 11:16:56 -0700
|
||||
Subject: [PATCH] encoding/pem: make Decode complexity linear
|
||||
|
||||
Because Decode scanned the input first for the first BEGIN line, and
|
||||
then the first END line, the complexity of Decode is quadratic. If the
|
||||
input contained a large number of BEGINs and then a single END right at
|
||||
the end of the input, we would find the first BEGIN, and then scan the
|
||||
entire input for the END, and fail to parse the block, so move onto the
|
||||
next BEGIN, scan the entire input for the END, etc.
|
||||
|
||||
Instead, look for the first END in the input, and then the first BEGIN
|
||||
that precedes the found END. We then process the bytes between the BEGIN
|
||||
and END, and move onto the bytes after the END for further processing.
|
||||
This gives us linear complexity.
|
||||
|
||||
Fixes CVE-2025-61723
|
||||
For #75676
|
||||
Fixes #75708
|
||||
|
||||
Change-Id: I813c4f63e78bca4054226c53e13865c781564ccf
|
||||
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2921
|
||||
Reviewed-by: Nicholas Husin <husin@google.com>
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2986
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/709842
|
||||
TryBot-Bypass: Michael Pratt <mpratt@google.com>
|
||||
Auto-Submit: Michael Pratt <mpratt@google.com>
|
||||
Reviewed-by: Carlos Amedee <carlos@golang.org>
|
||||
|
||||
CVE: CVE-2025-61723
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/74d4d836b91318a8764b94bc2b4b66ff599eb5f2]
|
||||
|
||||
Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
|
||||
---
|
||||
src/encoding/pem/pem.go | 67 ++++++++++++++++++++----------------
|
||||
src/encoding/pem/pem_test.go | 13 +++----
|
||||
2 files changed, 44 insertions(+), 36 deletions(-)
|
||||
|
||||
diff --git a/src/encoding/pem/pem.go b/src/encoding/pem/pem.go
|
||||
index 4b4f749..d365012 100644
|
||||
--- a/src/encoding/pem/pem.go
|
||||
+++ b/src/encoding/pem/pem.go
|
||||
@@ -37,7 +37,7 @@ type Block struct {
|
||||
// line bytes. The remainder of the byte array (also not including the new line
|
||||
// bytes) is also returned and this will always be smaller than the original
|
||||
// argument.
|
||||
-func getLine(data []byte) (line, rest []byte) {
|
||||
+func getLine(data []byte) (line, rest []byte, consumed int) {
|
||||
i := bytes.IndexByte(data, '\n')
|
||||
var j int
|
||||
if i < 0 {
|
||||
@@ -49,7 +49,7 @@ func getLine(data []byte) (line, rest []byte) {
|
||||
i--
|
||||
}
|
||||
}
|
||||
- return bytes.TrimRight(data[0:i], " \t"), data[j:]
|
||||
+ return bytes.TrimRight(data[0:i], " \t"), data[j:], j
|
||||
}
|
||||
|
||||
// removeSpacesAndTabs returns a copy of its input with all spaces and tabs
|
||||
@@ -90,20 +90,32 @@ func Decode(data []byte) (p *Block, rest []byte) {
|
||||
// pemStart begins with a newline. However, at the very beginning of
|
||||
// the byte array, we'll accept the start string without it.
|
||||
rest = data
|
||||
+
|
||||
for {
|
||||
- if bytes.HasPrefix(rest, pemStart[1:]) {
|
||||
- rest = rest[len(pemStart)-1:]
|
||||
- } else if _, after, ok := bytes.Cut(rest, pemStart); ok {
|
||||
- rest = after
|
||||
- } else {
|
||||
+ // Find the first END line, and then find the last BEGIN line before
|
||||
+ // the end line. This lets us skip any repeated BEGIN lines that don't
|
||||
+ // have a matching END.
|
||||
+ endIndex := bytes.Index(rest, pemEnd)
|
||||
+ if endIndex < 0 {
|
||||
+ return nil, data
|
||||
+ }
|
||||
+ endTrailerIndex := endIndex + len(pemEnd)
|
||||
+ beginIndex := bytes.LastIndex(rest[:endIndex], pemStart[1:])
|
||||
+ if beginIndex < 0 || beginIndex > 0 && rest[beginIndex-1] != '\n' {
|
||||
return nil, data
|
||||
}
|
||||
+ rest = rest[beginIndex+len(pemStart)-1:]
|
||||
+ endIndex -= beginIndex + len(pemStart) - 1
|
||||
+ endTrailerIndex -= beginIndex + len(pemStart) - 1
|
||||
|
||||
var typeLine []byte
|
||||
- typeLine, rest = getLine(rest)
|
||||
+ var consumed int
|
||||
+ typeLine, rest, consumed = getLine(rest)
|
||||
if !bytes.HasSuffix(typeLine, pemEndOfLine) {
|
||||
continue
|
||||
}
|
||||
+ endIndex -= consumed
|
||||
+ endTrailerIndex -= consumed
|
||||
typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
|
||||
|
||||
p = &Block{
|
||||
@@ -117,7 +129,7 @@ func Decode(data []byte) (p *Block, rest []byte) {
|
||||
if len(rest) == 0 {
|
||||
return nil, data
|
||||
}
|
||||
- line, next := getLine(rest)
|
||||
+ line, next, consumed := getLine(rest)
|
||||
|
||||
key, val, ok := bytes.Cut(line, colon)
|
||||
if !ok {
|
||||
@@ -129,21 +141,13 @@ func Decode(data []byte) (p *Block, rest []byte) {
|
||||
val = bytes.TrimSpace(val)
|
||||
p.Headers[string(key)] = string(val)
|
||||
rest = next
|
||||
+ endIndex -= consumed
|
||||
+ endTrailerIndex -= consumed
|
||||
}
|
||||
|
||||
- var endIndex, endTrailerIndex int
|
||||
-
|
||||
- // If there were no headers, the END line might occur
|
||||
- // immediately, without a leading newline.
|
||||
- if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) {
|
||||
- endIndex = 0
|
||||
- endTrailerIndex = len(pemEnd) - 1
|
||||
- } else {
|
||||
- endIndex = bytes.Index(rest, pemEnd)
|
||||
- endTrailerIndex = endIndex + len(pemEnd)
|
||||
- }
|
||||
-
|
||||
- if endIndex < 0 {
|
||||
+ // If there were headers, there must be a newline between the headers
|
||||
+ // and the END line, so endIndex should be >= 0.
|
||||
+ if len(p.Headers) > 0 && endIndex < 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -163,21 +167,24 @@ func Decode(data []byte) (p *Block, rest []byte) {
|
||||
}
|
||||
|
||||
// The line must end with only whitespace.
|
||||
- if s, _ := getLine(restOfEndLine); len(s) != 0 {
|
||||
+ if s, _, _ := getLine(restOfEndLine); len(s) != 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
- base64Data := removeSpacesAndTabs(rest[:endIndex])
|
||||
- p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
|
||||
- n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
|
||||
- if err != nil {
|
||||
- continue
|
||||
+ p.Bytes = []byte{}
|
||||
+ if endIndex > 0 {
|
||||
+ base64Data := removeSpacesAndTabs(rest[:endIndex])
|
||||
+ p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
|
||||
+ n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
|
||||
+ if err != nil {
|
||||
+ continue
|
||||
+ }
|
||||
+ p.Bytes = p.Bytes[:n]
|
||||
}
|
||||
- p.Bytes = p.Bytes[:n]
|
||||
|
||||
// the -1 is because we might have only matched pemEnd without the
|
||||
// leading newline if the PEM block was empty.
|
||||
- _, rest = getLine(rest[endIndex+len(pemEnd)-1:])
|
||||
+ _, rest, _ = getLine(rest[endIndex+len(pemEnd)-1:])
|
||||
return p, rest
|
||||
}
|
||||
}
|
||||
diff --git a/src/encoding/pem/pem_test.go b/src/encoding/pem/pem_test.go
|
||||
index 56a7754..7025277 100644
|
||||
--- a/src/encoding/pem/pem_test.go
|
||||
+++ b/src/encoding/pem/pem_test.go
|
||||
@@ -34,7 +34,7 @@ var getLineTests = []GetLineTest{
|
||||
|
||||
func TestGetLine(t *testing.T) {
|
||||
for i, test := range getLineTests {
|
||||
- x, y := getLine([]byte(test.in))
|
||||
+ x, y, _ := getLine([]byte(test.in))
|
||||
if string(x) != test.out1 || string(y) != test.out2 {
|
||||
t.Errorf("#%d got:%+v,%+v want:%s,%s", i, x, y, test.out1, test.out2)
|
||||
}
|
||||
@@ -46,6 +46,7 @@ func TestDecode(t *testing.T) {
|
||||
if !reflect.DeepEqual(result, certificate) {
|
||||
t.Errorf("#0 got:%#v want:%#v", result, certificate)
|
||||
}
|
||||
+
|
||||
result, remainder = Decode(remainder)
|
||||
if !reflect.DeepEqual(result, privateKey) {
|
||||
t.Errorf("#1 got:%#v want:%#v", result, privateKey)
|
||||
@@ -68,7 +69,7 @@ func TestDecode(t *testing.T) {
|
||||
}
|
||||
|
||||
result, remainder = Decode(remainder)
|
||||
- if result == nil || result.Type != "HEADERS" || len(result.Headers) != 1 {
|
||||
+ if result == nil || result.Type != "VALID HEADERS" || len(result.Headers) != 1 {
|
||||
t.Errorf("#5 expected single header block but got :%v", result)
|
||||
}
|
||||
|
||||
@@ -381,15 +382,15 @@ ZWAaUoVtWIQ52aKS0p19G99hhb+IVANC4akkdHV4SP8i7MVNZhfUmg==
|
||||
|
||||
# This shouldn't be recognised because of the missing newline after the
|
||||
headers.
|
||||
------BEGIN HEADERS-----
|
||||
+-----BEGIN INVALID HEADERS-----
|
||||
Header: 1
|
||||
------END HEADERS-----
|
||||
+-----END INVALID HEADERS-----
|
||||
|
||||
# This should be valid, however.
|
||||
------BEGIN HEADERS-----
|
||||
+-----BEGIN VALID HEADERS-----
|
||||
Header: 1
|
||||
|
||||
------END HEADERS-----`)
|
||||
+-----END VALID HEADERS-----`)
|
||||
|
||||
var certificate = &Block{Type: "CERTIFICATE",
|
||||
Headers: map[string]string{},
|
||||
--
|
||||
2.40.0
|
||||
75
meta/recipes-devtools/go/go/CVE-2025-61724.patch
Normal file
75
meta/recipes-devtools/go/go/CVE-2025-61724.patch
Normal file
@@ -0,0 +1,75 @@
|
||||
From a402f4ad285514f5f3db90516d72047d591b307a Mon Sep 17 00:00:00 2001
|
||||
From: Damien Neil <dneil@google.com>
|
||||
Date: Tue, 30 Sep 2025 15:11:16 -0700
|
||||
Subject: [PATCH] net/textproto: avoid quadratic complexity in
|
||||
Reader.ReadResponse
|
||||
|
||||
Reader.ReadResponse constructed a response string from repeated
|
||||
string concatenation, permitting a malicious sender to cause excessive
|
||||
memory allocation and CPU consumption by sending a response consisting
|
||||
of many short lines.
|
||||
|
||||
Use a strings.Builder to construct the string instead.
|
||||
|
||||
Thanks to Jakub Ciolek for reporting this issue.
|
||||
|
||||
Fixes CVE-2025-61724
|
||||
For #75716
|
||||
Fixes #75717
|
||||
|
||||
Change-Id: I1a98ce85a21b830cb25799f9ac9333a67400d736
|
||||
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2940
|
||||
Reviewed-by: Roland Shoemaker <bracewell@google.com>
|
||||
Reviewed-by: Nicholas Husin <husin@google.com>
|
||||
Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2980
|
||||
Reviewed-by: Damien Neil <dneil@google.com>
|
||||
Reviewed-on: https://go-review.googlesource.com/c/go/+/709837
|
||||
Reviewed-by: Carlos Amedee <carlos@golang.org>
|
||||
TryBot-Bypass: Michael Pratt <mpratt@google.com>
|
||||
Auto-Submit: Michael Pratt <mpratt@google.com>
|
||||
|
||||
CVE: CVE-2025-61724
|
||||
|
||||
Upstream-Status: Backport [https://github.com/golang/go/commit/a402f4ad285514f5f3db90516d72047d591b307a]
|
||||
|
||||
Signed-off-by: Archana Polampalli <archana.polampalli@windriver.com>
|
||||
---
|
||||
src/net/textproto/reader.go | 11 ++++++++---
|
||||
1 file changed, 8 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go
|
||||
index 7930211..0027efe 100644
|
||||
--- a/src/net/textproto/reader.go
|
||||
+++ b/src/net/textproto/reader.go
|
||||
@@ -283,8 +283,10 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err err
|
||||
//
|
||||
// An expectCode <= 0 disables the check of the status code.
|
||||
func (r *Reader) ReadResponse(expectCode int) (code int, message string, err error) {
|
||||
- code, continued, message, err := r.readCodeLine(expectCode)
|
||||
+ code, continued, first, err := r.readCodeLine(expectCode)
|
||||
multi := continued
|
||||
+ var messageBuilder strings.Builder
|
||||
+ messageBuilder.WriteString(first)
|
||||
for continued {
|
||||
line, err := r.ReadLine()
|
||||
if err != nil {
|
||||
@@ -295,12 +297,15 @@ func (r *Reader) ReadResponse(expectCode int) (code int, message string, err err
|
||||
var moreMessage string
|
||||
code2, continued, moreMessage, err = parseCodeLine(line, 0)
|
||||
if err != nil || code2 != code {
|
||||
- message += "\n" + strings.TrimRight(line, "\r\n")
|
||||
+ messageBuilder.WriteByte('\n')
|
||||
+ messageBuilder.WriteString(strings.TrimRight(line, "\r\n"))
|
||||
continued = true
|
||||
continue
|
||||
}
|
||||
- message += "\n" + moreMessage
|
||||
+ messageBuilder.WriteByte('\n')
|
||||
+ messageBuilder.WriteString(moreMessage)
|
||||
}
|
||||
+ message = messageBuilder.String()
|
||||
if err != nil && multi && message != "" {
|
||||
// replace one line error message with all lines (full message)
|
||||
err = &Error{code, message}
|
||||
--
|
||||
2.40.0
|
||||
@@ -0,0 +1,111 @@
|
||||
From ecd456ab88d379514b116ef9293318b74e5ed3ee Mon Sep 17 00:00:00 2001
|
||||
From: Martin Blech <78768+martinblech@users.noreply.github.com>
|
||||
Date: Thu, 4 Sep 2025 17:25:39 -0700
|
||||
Subject: [PATCH] Prevent XML injection: reject '<'/'>' in element/attr names
|
||||
(incl. @xmlns)
|
||||
|
||||
* Add tests for tag names, attribute names, and @xmlns prefixes; confirm attr values are escaped.
|
||||
|
||||
CVE: CVE-2025-9375
|
||||
|
||||
Upstream-Status: Backport
|
||||
https://github.com/martinblech/xmltodict/commit/ecd456ab88d379514b116ef9293318b74e5ed3ee
|
||||
https://git.launchpad.net/ubuntu/+source/python-xmltodict/commit/?id=e8110a20e00d80db31d5fc9f8f4577328385d6b6
|
||||
|
||||
Signed-off-by: Saravanan <saravanan.kadambathursubramaniyam@windriver.com>
|
||||
|
||||
---
|
||||
tests/test_dicttoxml.py | 32 ++++++++++++++++++++++++++++++++
|
||||
xmltodict.py | 20 +++++++++++++++++++-
|
||||
2 files changed, 51 insertions(+), 1 deletion(-)
|
||||
|
||||
Index: python-xmltodict-0.13.0/tests/test_dicttoxml.py
|
||||
===================================================================
|
||||
--- python-xmltodict-0.13.0.orig/tests/test_dicttoxml.py
|
||||
+++ python-xmltodict-0.13.0/tests/test_dicttoxml.py
|
||||
@@ -213,3 +213,35 @@ xmlns:b="http://b.com/"><x a:attr="val">
|
||||
expected_xml = '<?xml version="1.0" encoding="utf-8"?>\n<x>false</x>'
|
||||
xml = unparse(dict(x=False))
|
||||
self.assertEqual(xml, expected_xml)
|
||||
+
|
||||
+ def test_rejects_tag_name_with_angle_brackets(self):
|
||||
+ # Minimal guard: disallow '<' or '>' to prevent breaking tag context
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"m><tag>content</tag": "unsafe"}, full_document=False)
|
||||
+
|
||||
+ def test_rejects_attribute_name_with_angle_brackets(self):
|
||||
+ # Now we expect bad attribute names to be rejected
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse(
|
||||
+ {"a": {"@m><tag>content</tag": "unsafe", "#text": "x"}},
|
||||
+ full_document=False,
|
||||
+ )
|
||||
+
|
||||
+ def test_rejects_malicious_xmlns_prefix(self):
|
||||
+ # xmlns prefixes go under @xmlns mapping; reject angle brackets in prefix
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse(
|
||||
+ {
|
||||
+ "a": {
|
||||
+ "@xmlns": {"m><bad": "http://example.com/"},
|
||||
+ "#text": "x",
|
||||
+ }
|
||||
+ },
|
||||
+ full_document=False,
|
||||
+ )
|
||||
+
|
||||
+ def test_attribute_values_with_angle_brackets_are_escaped(self):
|
||||
+ # Attribute values should be escaped by XMLGenerator
|
||||
+ xml = unparse({"a": {"@attr": "1<middle>2", "#text": "x"}}, full_document=False)
|
||||
+ # The generated XML should contain escaped '<' and '>' within the attribute value
|
||||
+ self.assertIn('attr="1<middle>2"', xml)
|
||||
Index: python-xmltodict-0.13.0/xmltodict.py
|
||||
===================================================================
|
||||
--- python-xmltodict-0.13.0.orig/xmltodict.py
|
||||
+++ python-xmltodict-0.13.0/xmltodict.py
|
||||
@@ -379,6 +379,14 @@ def parse(xml_input, encoding=None, expa
|
||||
return handler.item
|
||||
|
||||
|
||||
+def _has_angle_brackets(value):
|
||||
+ """Return True if value (a str) contains '<' or '>'.
|
||||
+
|
||||
+ Non-string values return False. Uses fast substring checks implemented in C.
|
||||
+ """
|
||||
+ return isinstance(value, str) and ("<" in value or ">" in value)
|
||||
+
|
||||
+
|
||||
def _process_namespace(name, namespaces, ns_sep=':', attr_prefix='@'):
|
||||
if not namespaces:
|
||||
return name
|
||||
@@ -412,6 +420,9 @@ def _emit(key, value, content_handler,
|
||||
if result is None:
|
||||
return
|
||||
key, value = result
|
||||
+ # Minimal validation to avoid breaking out of tag context
|
||||
+ if _has_angle_brackets(key):
|
||||
+ raise ValueError('Invalid element name: "<" or ">" not allowed')
|
||||
if (not hasattr(value, '__iter__')
|
||||
or isinstance(value, _basestring)
|
||||
or isinstance(value, dict)):
|
||||
@@ -445,12 +456,19 @@ def _emit(key, value, content_handler,
|
||||
attr_prefix)
|
||||
if ik == '@xmlns' and isinstance(iv, dict):
|
||||
for k, v in iv.items():
|
||||
+ if _has_angle_brackets(k):
|
||||
+ raise ValueError(
|
||||
+ 'Invalid attribute name: "<" or ">" not allowed'
|
||||
+ )
|
||||
attr = 'xmlns{}'.format(':{}'.format(k) if k else '')
|
||||
attrs[attr] = _unicode(v)
|
||||
continue
|
||||
if not isinstance(iv, _unicode):
|
||||
iv = _unicode(iv)
|
||||
- attrs[ik[len(attr_prefix):]] = iv
|
||||
+ attr_name = ik[len(attr_prefix) :]
|
||||
+ if _has_angle_brackets(attr_name):
|
||||
+ raise ValueError('Invalid attribute name: "<" or ">" not allowed')
|
||||
+ attrs[attr_name] = iv
|
||||
continue
|
||||
children.append((ik, iv))
|
||||
if pretty:
|
||||
@@ -0,0 +1,176 @@
|
||||
From f98c90f071228ed73df997807298e1df4f790c33 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Blech <78768+martinblech@users.noreply.github.com>
|
||||
Date: Mon, 8 Sep 2025 11:18:33 -0700
|
||||
Subject: [PATCH] Enhance unparse() XML name validation with stricter rules and
|
||||
tests
|
||||
|
||||
Extend existing validation (previously only for "<" and ">") to also
|
||||
reject element, attribute, and xmlns prefix names that are non-string,
|
||||
start with "?" or "!", or contain "/", spaces, tabs, or newlines.
|
||||
Update _emit and namespace handling to use _validate_name. Add tests
|
||||
covering these new invalid name cases.
|
||||
|
||||
CVE: CVE-2025-9375
|
||||
|
||||
Upstream-Status: Backport
|
||||
https://github.com/martinblech/xmltodict/commit/f98c90f071228ed73df997807298e1df4f790c33
|
||||
https://git.launchpad.net/ubuntu/+source/python-xmltodict/commit/?id=e8110a20e00d80db31d5fc9f8f4577328385d6b6
|
||||
|
||||
Signed-off-by: Saravanan <saravanan.kadambathursubramaniyam@windriver.com
|
||||
---
|
||||
tests/test_dicttoxml.py | 60 +++++++++++++++++++++++++++++++++++++++++
|
||||
xmltodict.py | 48 ++++++++++++++++++++++++++-------
|
||||
2 files changed, 99 insertions(+), 9 deletions(-)
|
||||
|
||||
Index: python-xmltodict-0.13.0/tests/test_dicttoxml.py
|
||||
===================================================================
|
||||
--- python-xmltodict-0.13.0.orig/tests/test_dicttoxml.py
|
||||
+++ python-xmltodict-0.13.0/tests/test_dicttoxml.py
|
||||
@@ -245,3 +245,63 @@ xmlns:b="http://b.com/"><x a:attr="val">
|
||||
xml = unparse({"a": {"@attr": "1<middle>2", "#text": "x"}}, full_document=False)
|
||||
# The generated XML should contain escaped '<' and '>' within the attribute value
|
||||
self.assertIn('attr="1<middle>2"', xml)
|
||||
+
|
||||
+ def test_rejects_tag_name_starting_with_question(self):
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"?pi": "data"}, full_document=False)
|
||||
+
|
||||
+ def test_rejects_tag_name_starting_with_bang(self):
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"!decl": "data"}, full_document=False)
|
||||
+
|
||||
+ def test_rejects_attribute_name_starting_with_question(self):
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"a": {"@?weird": "x"}}, full_document=False)
|
||||
+
|
||||
+ def test_rejects_attribute_name_starting_with_bang(self):
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"a": {"@!weird": "x"}}, full_document=False)
|
||||
+
|
||||
+ def test_rejects_xmlns_prefix_starting_with_question_or_bang(self):
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"a": {"@xmlns": {"?p": "http://e/"}}}, full_document=False)
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"a": {"@xmlns": {"!p": "http://e/"}}}, full_document=False)
|
||||
+
|
||||
+ def test_rejects_non_string_names(self):
|
||||
+ class Weird:
|
||||
+ def __str__(self):
|
||||
+ return "bad>name"
|
||||
+
|
||||
+ # Non-string element key
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({Weird(): "x"}, full_document=False)
|
||||
+ # Non-string attribute key
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"a": {Weird(): "x"}}, full_document=False)
|
||||
+
|
||||
+ def test_rejects_tag_name_with_slash(self):
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"bad/name": "x"}, full_document=False)
|
||||
+
|
||||
+ def test_rejects_tag_name_with_whitespace(self):
|
||||
+ for name in ["bad name", "bad\tname", "bad\nname"]:
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({name: "x"}, full_document=False)
|
||||
+
|
||||
+ def test_rejects_attribute_name_with_slash(self):
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"a": {"@bad/name": "x"}}, full_document=False)
|
||||
+
|
||||
+ def test_rejects_attribute_name_with_whitespace(self):
|
||||
+ for name in ["@bad name", "@bad\tname", "@bad\nname"]:
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"a": {name: "x"}}, full_document=False)
|
||||
+
|
||||
+ def test_rejects_xmlns_prefix_with_slash_or_whitespace(self):
|
||||
+ # Slash
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"a": {"@xmlns": {"bad/prefix": "http://e/"}}}, full_document=False)
|
||||
+ # Whitespace
|
||||
+ with self.assertRaises(ValueError):
|
||||
+ unparse({"a": {"@xmlns": {"bad prefix": "http://e/"}}}, full_document=False)
|
||||
Index: python-xmltodict-0.13.0/xmltodict.py
|
||||
===================================================================
|
||||
--- python-xmltodict-0.13.0.orig/xmltodict.py
|
||||
+++ python-xmltodict-0.13.0/xmltodict.py
|
||||
@@ -387,7 +387,42 @@ def _has_angle_brackets(value):
|
||||
return isinstance(value, str) and ("<" in value or ">" in value)
|
||||
|
||||
|
||||
+def _has_invalid_name_chars(value):
|
||||
+ """Return True if value (a str) contains any disallowed name characters.
|
||||
+
|
||||
+ Disallowed: '<', '>', '/', or any whitespace character.
|
||||
+ Non-string values return False.
|
||||
+ """
|
||||
+ if not isinstance(value, str):
|
||||
+ return False
|
||||
+ if "<" in value or ">" in value or "/" in value:
|
||||
+ return True
|
||||
+ # Check for any whitespace (spaces, tabs, newlines, etc.)
|
||||
+ return any(ch.isspace() for ch in value)
|
||||
+
|
||||
+
|
||||
+def _validate_name(value, kind):
|
||||
+ """Validate an element/attribute name for XML safety.
|
||||
+
|
||||
+ Raises ValueError with a specific reason when invalid.
|
||||
+
|
||||
+ kind: 'element' or 'attribute' (used in error messages)
|
||||
+ """
|
||||
+ if not isinstance(value, str):
|
||||
+ raise ValueError(f"{kind} name must be a string")
|
||||
+ if value.startswith("?") or value.startswith("!"):
|
||||
+ raise ValueError(f'Invalid {kind} name: cannot start with "?" or "!"')
|
||||
+ if "<" in value or ">" in value:
|
||||
+ raise ValueError(f'Invalid {kind} name: "<" or ">" not allowed')
|
||||
+ if "/" in value:
|
||||
+ raise ValueError(f'Invalid {kind} name: "/" not allowed')
|
||||
+ if any(ch.isspace() for ch in value):
|
||||
+ raise ValueError(f"Invalid {kind} name: whitespace not allowed")
|
||||
+
|
||||
+
|
||||
def _process_namespace(name, namespaces, ns_sep=':', attr_prefix='@'):
|
||||
+ if not isinstance(name, str):
|
||||
+ return name
|
||||
if not namespaces:
|
||||
return name
|
||||
try:
|
||||
@@ -421,8 +456,7 @@ def _emit(key, value, content_handler,
|
||||
return
|
||||
key, value = result
|
||||
# Minimal validation to avoid breaking out of tag context
|
||||
- if _has_angle_brackets(key):
|
||||
- raise ValueError('Invalid element name: "<" or ">" not allowed')
|
||||
+ _validate_name(key, "element")
|
||||
if (not hasattr(value, '__iter__')
|
||||
or isinstance(value, _basestring)
|
||||
or isinstance(value, dict)):
|
||||
@@ -451,23 +485,19 @@ def _emit(key, value, content_handler,
|
||||
if ik == cdata_key:
|
||||
cdata = iv
|
||||
continue
|
||||
- if ik.startswith(attr_prefix):
|
||||
+ if isinstance(ik, str) and ik.startswith(attr_prefix):
|
||||
ik = _process_namespace(ik, namespaces, namespace_separator,
|
||||
attr_prefix)
|
||||
if ik == '@xmlns' and isinstance(iv, dict):
|
||||
for k, v in iv.items():
|
||||
- if _has_angle_brackets(k):
|
||||
- raise ValueError(
|
||||
- 'Invalid attribute name: "<" or ">" not allowed'
|
||||
- )
|
||||
+ _validate_name(k, "attribute")
|
||||
attr = 'xmlns{}'.format(':{}'.format(k) if k else '')
|
||||
attrs[attr] = _unicode(v)
|
||||
continue
|
||||
if not isinstance(iv, _unicode):
|
||||
iv = _unicode(iv)
|
||||
attr_name = ik[len(attr_prefix) :]
|
||||
- if _has_angle_brackets(attr_name):
|
||||
- raise ValueError('Invalid attribute name: "<" or ">" not allowed')
|
||||
+ _validate_name(attr_name, "attribute")
|
||||
attrs[attr_name] = iv
|
||||
continue
|
||||
children.append((ik, iv))
|
||||
@@ -13,6 +13,8 @@ inherit pypi setuptools3 ptest
|
||||
|
||||
SRC_URI += " \
|
||||
file://run-ptest \
|
||||
file://CVE-2025-9375-1.patch \
|
||||
file://CVE-2025-9375-2.patch \
|
||||
"
|
||||
|
||||
RDEPENDS:${PN} += " \
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user