summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Marko <peter.marko@siemens.com>2025-05-20 22:20:33 +0200
committerSteve Sakoman <steve@sakoman.com>2025-06-02 10:26:30 -0700
commit32dddbf6b473e82c490292fead35e4d4e585f802 (patch)
tree93d86d903e42eb903b81a459f6e1fcf00d9e2bb3
parentfdb3f69e746ea2a3e37811bd627352bb9cb28af4 (diff)
downloadpoky-32dddbf6b473e82c490292fead35e4d4e585f802.tar.gz
xz: patch CVE-2025-31115
Cherry-pick commits from [1] linked from [2] from branch v5.6 [1] https://tukaani.org/xz/xz-cve-2025-31115.patch [2] https://tukaani.org/xz/threaded-decoder-early-free.html (From OE-Core rev: 7c5d0f0e1830095d3e8c30c648081b5e52b0ef06) Signed-off-by: Peter Marko <peter.marko@siemens.com> Signed-off-by: Steve Sakoman <steve@sakoman.com>
-rw-r--r--meta/recipes-extended/xz/xz/CVE-2025-31115-01.patch29
-rw-r--r--meta/recipes-extended/xz/xz/CVE-2025-31115-02.patch152
-rw-r--r--meta/recipes-extended/xz/xz/CVE-2025-31115-03.patch98
-rw-r--r--meta/recipes-extended/xz/xz/CVE-2025-31115-04.patch56
-rw-r--r--meta/recipes-extended/xz/xz_5.6.4.bb4
5 files changed, 339 insertions, 0 deletions
diff --git a/meta/recipes-extended/xz/xz/CVE-2025-31115-01.patch b/meta/recipes-extended/xz/xz/CVE-2025-31115-01.patch
new file mode 100644
index 0000000000..d6e75f8201
--- /dev/null
+++ b/meta/recipes-extended/xz/xz/CVE-2025-31115-01.patch
@@ -0,0 +1,29 @@
1From c1a91b8baeb947c5b232a6c3d6319267131830bc Mon Sep 17 00:00:00 2001
2From: Lasse Collin <lasse.collin@tukaani.org>
3Date: Thu, 3 Apr 2025 14:34:42 +0300
4Subject: [PATCH 1/4] liblzma: mt dec: Fix a comment
5
6Reviewed-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
7Thanks-to: Sam James <sam@gentoo.org>
8(cherry picked from commit 831b55b971cf579ee16a854f177c36b20d3c6999)
9
10CVE: CVE-2025-31115
11Upstream-Status: Backport [https://github.com/tukaani-project/xz/commit/c1a91b8baeb947c5b232a6c3d6319267131830bc]
12Signed-off-by: Peter Marko <peter.marko@siemens.com>
13---
14 src/liblzma/common/stream_decoder_mt.c | 2 +-
15 1 file changed, 1 insertion(+), 1 deletion(-)
16
17diff --git a/src/liblzma/common/stream_decoder_mt.c b/src/liblzma/common/stream_decoder_mt.c
18index 244624a4..6f06f1d1 100644
19--- a/src/liblzma/common/stream_decoder_mt.c
20+++ b/src/liblzma/common/stream_decoder_mt.c
21@@ -347,7 +347,7 @@ worker_enable_partial_update(void *thr_ptr)
22
23
24 /// Things do to at THR_STOP or when finishing a Block.
25-/// This is called with thr->mutex locked.
26+/// This is called with thr->coder->mutex locked.
27 static void
28 worker_stop(struct worker_thread *thr)
29 {
diff --git a/meta/recipes-extended/xz/xz/CVE-2025-31115-02.patch b/meta/recipes-extended/xz/xz/CVE-2025-31115-02.patch
new file mode 100644
index 0000000000..7b36ae551a
--- /dev/null
+++ b/meta/recipes-extended/xz/xz/CVE-2025-31115-02.patch
@@ -0,0 +1,152 @@
1From f74cf18ad084a9185d8ae148d89265860aa8004c Mon Sep 17 00:00:00 2001
2From: Lasse Collin <lasse.collin@tukaani.org>
3Date: Thu, 3 Apr 2025 14:34:42 +0300
4Subject: [PATCH 2/4] liblzma: mt dec: Simplify by removing the THR_STOP state
5
6The main thread can directly set THR_IDLE in threads_stop() which is
7called when errors are detected. threads_stop() won't return the stopped
8threads to the pool or free the memory pointed by thr->in anymore, but
9it doesn't matter because the existing workers won't be reused after
10an error. The resources will be cleaned up when threads_end() is
11called (reinitializing the decoder always calls threads_end()).
12
13Reviewed-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
14Thanks-to: Sam James <sam@gentoo.org>
15(cherry picked from commit c0c835964dfaeb2513a3c0bdb642105152fe9f34)
16
17CVE: CVE-2025-31115
18Upstream-Status: Backport [https://github.com/tukaani-project/xz/commit/f74cf18ad084a9185d8ae148d89265860aa8004c]
19Signed-off-by: Peter Marko <peter.marko@siemens.com>
20---
21 src/liblzma/common/stream_decoder_mt.c | 75 ++++++++++----------------
22 1 file changed, 29 insertions(+), 46 deletions(-)
23
24diff --git a/src/liblzma/common/stream_decoder_mt.c b/src/liblzma/common/stream_decoder_mt.c
25index 6f06f1d1..e1d07007 100644
26--- a/src/liblzma/common/stream_decoder_mt.c
27+++ b/src/liblzma/common/stream_decoder_mt.c
28@@ -23,15 +23,10 @@ typedef enum {
29 THR_IDLE,
30
31 /// Decoding is in progress.
32- /// Main thread may change this to THR_STOP or THR_EXIT.
33+ /// Main thread may change this to THR_IDLE or THR_EXIT.
34 /// The worker thread may change this to THR_IDLE.
35 THR_RUN,
36
37- /// The main thread wants the thread to stop whatever it was doing
38- /// but not exit. Main thread may change this to THR_EXIT.
39- /// The worker thread may change this to THR_IDLE.
40- THR_STOP,
41-
42 /// The main thread wants the thread to exit.
43 THR_EXIT,
44
45@@ -346,27 +341,6 @@ worker_enable_partial_update(void *thr_ptr)
46 }
47
48
49-/// Things do to at THR_STOP or when finishing a Block.
50-/// This is called with thr->coder->mutex locked.
51-static void
52-worker_stop(struct worker_thread *thr)
53-{
54- // Update memory usage counters.
55- thr->coder->mem_in_use -= thr->in_size;
56- thr->in_size = 0; // thr->in was freed above.
57-
58- thr->coder->mem_in_use -= thr->mem_filters;
59- thr->coder->mem_cached += thr->mem_filters;
60-
61- // Put this thread to the stack of free threads.
62- thr->next = thr->coder->threads_free;
63- thr->coder->threads_free = thr;
64-
65- mythread_cond_signal(&thr->coder->cond);
66- return;
67-}
68-
69-
70 static MYTHREAD_RET_TYPE
71 worker_decoder(void *thr_ptr)
72 {
73@@ -397,17 +371,6 @@ next_loop_unlocked:
74 return MYTHREAD_RET_VALUE;
75 }
76
77- if (thr->state == THR_STOP) {
78- thr->state = THR_IDLE;
79- mythread_mutex_unlock(&thr->mutex);
80-
81- mythread_sync(thr->coder->mutex) {
82- worker_stop(thr);
83- }
84-
85- goto next_loop_lock;
86- }
87-
88 assert(thr->state == THR_RUN);
89
90 // Update progress info for get_progress().
91@@ -510,7 +473,22 @@ next_loop_unlocked:
92 && thr->coder->thread_error == LZMA_OK)
93 thr->coder->thread_error = ret;
94
95- worker_stop(thr);
96+ // Return the worker thread to the stack of available
97+ // threads.
98+ {
99+ // Update memory usage counters.
100+ thr->coder->mem_in_use -= thr->in_size;
101+ thr->in_size = 0; // thr->in was freed above.
102+
103+ thr->coder->mem_in_use -= thr->mem_filters;
104+ thr->coder->mem_cached += thr->mem_filters;
105+
106+ // Put this thread to the stack of free threads.
107+ thr->next = thr->coder->threads_free;
108+ thr->coder->threads_free = thr;
109+ }
110+
111+ mythread_cond_signal(&thr->coder->cond);
112 }
113
114 goto next_loop_lock;
115@@ -544,17 +522,22 @@ threads_end(struct lzma_stream_coder *coder, const lzma_allocator *allocator)
116 }
117
118
119+/// Tell worker threads to stop without doing any cleaning up.
120+/// The clean up will be done when threads_exit() is called;
121+/// it's not possible to reuse the threads after threads_stop().
122+///
123+/// This is called before returning an unrecoverable error code
124+/// to the application. It would be waste of processor time
125+/// to keep the threads running in such a situation.
126 static void
127 threads_stop(struct lzma_stream_coder *coder)
128 {
129 for (uint32_t i = 0; i < coder->threads_initialized; ++i) {
130+ // The threads that are in the THR_RUN state will stop
131+ // when they check the state the next time. There's no
132+ // need to signal coder->threads[i].cond.
133 mythread_sync(coder->threads[i].mutex) {
134- // The state must be changed conditionally because
135- // THR_IDLE -> THR_STOP is not a valid state change.
136- if (coder->threads[i].state != THR_IDLE) {
137- coder->threads[i].state = THR_STOP;
138- mythread_cond_signal(&coder->threads[i].cond);
139- }
140+ coder->threads[i].state = THR_IDLE;
141 }
142 }
143
144@@ -1948,7 +1931,7 @@ stream_decoder_mt_init(lzma_next_coder *next, const lzma_allocator *allocator,
145 // accounting from scratch, too. Changes in filter and block sizes may
146 // affect number of threads.
147 //
148- // FIXME? Reusing should be easy but unlike the single-threaded
149+ // Reusing threads doesn't seem worth it. Unlike the single-threaded
150 // decoder, with some types of input file combinations reusing
151 // could leave quite a lot of memory allocated but unused (first
152 // file could allocate a lot, the next files could use fewer
diff --git a/meta/recipes-extended/xz/xz/CVE-2025-31115-03.patch b/meta/recipes-extended/xz/xz/CVE-2025-31115-03.patch
new file mode 100644
index 0000000000..892249d0b4
--- /dev/null
+++ b/meta/recipes-extended/xz/xz/CVE-2025-31115-03.patch
@@ -0,0 +1,98 @@
1From 1b874b4f04909b7bb5259cb612ecef39a434dde8 Mon Sep 17 00:00:00 2001
2From: Lasse Collin <lasse.collin@tukaani.org>
3Date: Thu, 3 Apr 2025 14:34:42 +0300
4Subject: [PATCH 3/4] liblzma: mt dec: Don't free the input buffer too early
5 (CVE-2025-31115)
6
7The input buffer must be valid as long as the main thread is writing
8to the worker-specific input buffer. Fix it by making the worker
9thread not free the buffer on errors and not return the worker thread to
10the pool. The input buffer will be freed when threads_end() is called.
11
12With invalid input, the bug could at least result in a crash. The
13effects include heap use after free and writing to an address based
14on the null pointer plus an offset.
15
16The bug has been there since the first committed version of the threaded
17decoder and thus affects versions from 5.3.3alpha to 5.8.0.
18
19As the commit message in 4cce3e27f529 says, I had made significant
20changes on top of Sebastian's patch. This bug was indeed introduced
21by my changes; it wasn't in Sebastian's version.
22
23Thanks to Harri K. Koskinen for discovering and reporting this issue.
24
25Fixes: 4cce3e27f529 ("liblzma: Add threaded .xz decompressor.")
26Reported-by: Harri K. Koskinen <x64nop@nannu.org>
27Reviewed-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
28Thanks-to: Sam James <sam@gentoo.org>
29(cherry picked from commit d5a2ffe41bb77b918a8c96084885d4dbe4bf6480)
30
31CVE: CVE-2025-31115
32Upstream-Status: Backport [https://github.com/tukaani-project/xz/commit/1b874b4f04909b7bb5259cb612ecef39a434dde8]
33Signed-off-by: Peter Marko <peter.marko@siemens.com>
34---
35 src/liblzma/common/stream_decoder_mt.c | 31 ++++++++++++++++++--------
36 1 file changed, 22 insertions(+), 9 deletions(-)
37
38diff --git a/src/liblzma/common/stream_decoder_mt.c b/src/liblzma/common/stream_decoder_mt.c
39index e1d07007..ce5e54ac 100644
40--- a/src/liblzma/common/stream_decoder_mt.c
41+++ b/src/liblzma/common/stream_decoder_mt.c
42@@ -435,8 +435,7 @@ next_loop_unlocked:
43 }
44
45 // Either we finished successfully (LZMA_STREAM_END) or an error
46- // occurred. Both cases are handled almost identically. The error
47- // case requires updating thr->coder->thread_error.
48+ // occurred.
49 //
50 // The sizes are in the Block Header and the Block decoder
51 // checks that they match, thus we know these:
52@@ -444,16 +443,30 @@ next_loop_unlocked:
53 assert(ret != LZMA_STREAM_END
54 || thr->out_pos == thr->block_options.uncompressed_size);
55
56- // Free the input buffer. Don't update in_size as we need
57- // it later to update thr->coder->mem_in_use.
58- lzma_free(thr->in, thr->allocator);
59- thr->in = NULL;
60-
61 mythread_sync(thr->mutex) {
62+ // Block decoder ensures this, but do a sanity check anyway
63+ // because thr->in_filled < thr->in_size means that the main
64+ // thread is still writing to thr->in.
65+ if (ret == LZMA_STREAM_END && thr->in_filled != thr->in_size) {
66+ assert(0);
67+ ret = LZMA_PROG_ERROR;
68+ }
69+
70 if (thr->state != THR_EXIT)
71 thr->state = THR_IDLE;
72 }
73
74+ // Free the input buffer. Don't update in_size as we need
75+ // it later to update thr->coder->mem_in_use.
76+ //
77+ // This step is skipped if an error occurred because the main thread
78+ // might still be writing to thr->in. The memory will be freed after
79+ // threads_end() sets thr->state = THR_EXIT.
80+ if (ret == LZMA_STREAM_END) {
81+ lzma_free(thr->in, thr->allocator);
82+ thr->in = NULL;
83+ }
84+
85 mythread_sync(thr->coder->mutex) {
86 // Move our progress info to the main thread.
87 thr->coder->progress_in += thr->in_pos;
88@@ -474,8 +487,8 @@ next_loop_unlocked:
89 thr->coder->thread_error = ret;
90
91 // Return the worker thread to the stack of available
92- // threads.
93- {
94+ // threads only if no errors occurred.
95+ if (ret == LZMA_STREAM_END) {
96 // Update memory usage counters.
97 thr->coder->mem_in_use -= thr->in_size;
98 thr->in_size = 0; // thr->in was freed above.
diff --git a/meta/recipes-extended/xz/xz/CVE-2025-31115-04.patch b/meta/recipes-extended/xz/xz/CVE-2025-31115-04.patch
new file mode 100644
index 0000000000..f80daceb4a
--- /dev/null
+++ b/meta/recipes-extended/xz/xz/CVE-2025-31115-04.patch
@@ -0,0 +1,56 @@
1From 6ff5b8c55960f9ebc917b668bd3567ef217175fa Mon Sep 17 00:00:00 2001
2From: Lasse Collin <lasse.collin@tukaani.org>
3Date: Thu, 3 Apr 2025 14:34:42 +0300
4Subject: [PATCH 4/4] liblzma: mt dec: Don't modify thr->in_size in the worker
5 thread
6
7Don't set thr->in_size = 0 when returning the thread to the stack of
8available threads. Not only is it useless, but the main thread may
9read the value in SEQ_BLOCK_THR_RUN. With valid inputs, it made
10no difference if the main thread saw the original value or 0. With
11invalid inputs (when worker thread stops early), thr->in_size was
12no longer modified after the previous commit with the security fix
13("Don't free the input buffer too early").
14
15So while the bug appears harmless now, it's important to fix it because
16the variable was being modified without proper locking. It's trivial
17to fix because there is no need to change the value. Only main thread
18needs to set the value in (in SEQ_BLOCK_THR_INIT) when starting a new
19Block before the worker thread is activated.
20
21Fixes: 4cce3e27f529 ("liblzma: Add threaded .xz decompressor.")
22Reviewed-by: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
23Thanks-to: Sam James <sam@gentoo.org>
24(cherry picked from commit 8188048854e8d11071b8a50d093c74f4c030acc9)
25
26CVE: CVE-2025-31115
27Upstream-Status: Backport [https://github.com/tukaani-project/xz/commit/6ff5b8c55960f9ebc917b668bd3567ef217175fa]
28Signed-off-by: Peter Marko <peter.marko@siemens.com>
29---
30 src/liblzma/common/stream_decoder_mt.c | 6 ++++--
31 1 file changed, 4 insertions(+), 2 deletions(-)
32
33diff --git a/src/liblzma/common/stream_decoder_mt.c b/src/liblzma/common/stream_decoder_mt.c
34index ce5e54ac..0cdb47d3 100644
35--- a/src/liblzma/common/stream_decoder_mt.c
36+++ b/src/liblzma/common/stream_decoder_mt.c
37@@ -491,8 +491,6 @@ next_loop_unlocked:
38 if (ret == LZMA_STREAM_END) {
39 // Update memory usage counters.
40 thr->coder->mem_in_use -= thr->in_size;
41- thr->in_size = 0; // thr->in was freed above.
42-
43 thr->coder->mem_in_use -= thr->mem_filters;
44 thr->coder->mem_cached += thr->mem_filters;
45
46@@ -1557,6 +1555,10 @@ stream_decode_mt(void *coder_ptr, const lzma_allocator *allocator,
47 }
48
49 // Return if the input didn't contain the whole Block.
50+ //
51+ // NOTE: When we updated coder->thr->in_filled a few lines
52+ // above, the worker thread might by now have finished its
53+ // work and returned itself back to the stack of free threads.
54 if (coder->thr->in_filled < coder->thr->in_size) {
55 assert(*in_pos == in_size);
56 return LZMA_OK;
diff --git a/meta/recipes-extended/xz/xz_5.6.4.bb b/meta/recipes-extended/xz/xz_5.6.4.bb
index e48f4dbd7f..52bfd844b2 100644
--- a/meta/recipes-extended/xz/xz_5.6.4.bb
+++ b/meta/recipes-extended/xz/xz_5.6.4.bb
@@ -27,6 +27,10 @@ LIC_FILES_CHKSUM = "file://COPYING;md5=c02de712b028a5cc7e22472e8f2b3db1 \
27 27
28SRC_URI = "https://github.com/tukaani-project/xz/releases/download/v${PV}/xz-${PV}.tar.gz \ 28SRC_URI = "https://github.com/tukaani-project/xz/releases/download/v${PV}/xz-${PV}.tar.gz \
29 file://run-ptest \ 29 file://run-ptest \
30 file://CVE-2025-31115-01.patch \
31 file://CVE-2025-31115-02.patch \
32 file://CVE-2025-31115-03.patch \
33 file://CVE-2025-31115-04.patch \
30 " 34 "
31SRC_URI[sha256sum] = "269e3f2e512cbd3314849982014dc199a7b2148cf5c91cedc6db629acdf5e09b" 35SRC_URI[sha256sum] = "269e3f2e512cbd3314849982014dc199a7b2148cf5c91cedc6db629acdf5e09b"
32UPSTREAM_CHECK_REGEX = "releases/tag/v(?P<pver>\d+(\.\d+)+)" 36UPSTREAM_CHECK_REGEX = "releases/tag/v(?P<pver>\d+(\.\d+)+)"