diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,7 @@
+## 5.8.1
+
+* Update to 5.8.1 upstream sources, fixing [CVE-2025-31115](https://tukaani.org/xz/#_cve_2025_31115_threaded_xz_decoder_frees_memory_too_early)
+
 ## 5.8.0.1
 
 * Fix linking issues due to including `tuklib_mbstr_nonprint.c` and `tuklib_mbstr_wrap.c`
diff --git a/cbits/common/sysdefs.h b/cbits/common/sysdefs.h
--- a/cbits/common/sysdefs.h
+++ b/cbits/common/sysdefs.h
@@ -172,7 +172,9 @@
 #if __STDC_VERSION__ >= 202311
 	// alignas is a keyword in C23. Do nothing.
 #elif __STDC_VERSION__ >= 201112
-#	include <stdalign.h>
+	// Oracle Developer Studio 12.6 lacks <stdalign.h>.
+	// For simplicity, avoid the header with all C11/C17 compilers.
+#	define alignas _Alignas
 #elif defined(__GNUC__) || defined(__clang__)
 #	define alignas(n) __attribute__((__aligned__(n)))
 #else
diff --git a/cbits/liblzma/api/lzma/version.h b/cbits/liblzma/api/lzma/version.h
--- a/cbits/liblzma/api/lzma/version.h
+++ b/cbits/liblzma/api/lzma/version.h
@@ -22,7 +22,7 @@
 #define LZMA_VERSION_MINOR 8
 
 /** \brief Patch version number of the liblzma release. */
-#define LZMA_VERSION_PATCH 0
+#define LZMA_VERSION_PATCH 1
 
 /**
  * \brief Version stability marker
diff --git a/cbits/liblzma/common/common.c b/cbits/liblzma/common/common.c
--- a/cbits/liblzma/common/common.c
+++ b/cbits/liblzma/common/common.c
@@ -96,6 +96,12 @@
 		size_t in_size, uint8_t *restrict out,
 		size_t *restrict out_pos, size_t out_size)
 {
+	assert(in != NULL || *in_pos == in_size);
+	assert(out != NULL || *out_pos == out_size);
+
+	assert(*in_pos <= in_size);
+	assert(*out_pos <= out_size);
+
 	const size_t in_avail = in_size - *in_pos;
 	const size_t out_avail = out_size - *out_pos;
 	const size_t copy_size = my_min(in_avail, out_avail);
diff --git a/cbits/liblzma/common/stream_decoder_mt.c b/cbits/liblzma/common/stream_decoder_mt.c
--- a/cbits/liblzma/common/stream_decoder_mt.c
+++ b/cbits/liblzma/common/stream_decoder_mt.c
@@ -23,15 +23,10 @@
 	THR_IDLE,
 
 	/// Decoding is in progress.
-	/// Main thread may change this to THR_STOP or THR_EXIT.
+	/// Main thread may change this to THR_IDLE or THR_EXIT.
 	/// The worker thread may change this to THR_IDLE.
 	THR_RUN,
 
-	/// The main thread wants the thread to stop whatever it was doing
-	/// but not exit. Main thread may change this to THR_EXIT.
-	/// The worker thread may change this to THR_IDLE.
-	THR_STOP,
-
 	/// The main thread wants the thread to exit.
 	THR_EXIT,
 
@@ -346,27 +341,6 @@
 }
 
 
-/// Things do to at THR_STOP or when finishing a Block.
-/// This is called with thr->mutex locked.
-static void
-worker_stop(struct worker_thread *thr)
-{
-	// Update memory usage counters.
-	thr->coder->mem_in_use -= thr->in_size;
-	thr->in_size = 0; // thr->in was freed above.
-
-	thr->coder->mem_in_use -= thr->mem_filters;
-	thr->coder->mem_cached += thr->mem_filters;
-
-	// Put this thread to the stack of free threads.
-	thr->next = thr->coder->threads_free;
-	thr->coder->threads_free = thr;
-
-	mythread_cond_signal(&thr->coder->cond);
-	return;
-}
-
-
 static MYTHREAD_RET_TYPE
 worker_decoder(void *thr_ptr)
 {
@@ -397,17 +371,6 @@
 		return MYTHREAD_RET_VALUE;
 	}
 
-	if (thr->state == THR_STOP) {
-		thr->state = THR_IDLE;
-		mythread_mutex_unlock(&thr->mutex);
-
-		mythread_sync(thr->coder->mutex) {
-			worker_stop(thr);
-		}
-
-		goto next_loop_lock;
-	}
-
 	assert(thr->state == THR_RUN);
 
 	// Update progress info for get_progress().
@@ -472,8 +435,7 @@
 	}
 
 	// Either we finished successfully (LZMA_STREAM_END) or an error
-	// occurred. Both cases are handled almost identically. The error
-	// case requires updating thr->coder->thread_error.
+	// occurred.
 	//
 	// The sizes are in the Block Header and the Block decoder
 	// checks that they match, thus we know these:
@@ -481,16 +443,30 @@
 	assert(ret != LZMA_STREAM_END
 		|| thr->out_pos == thr->block_options.uncompressed_size);
 
-	// Free the input buffer. Don't update in_size as we need
-	// it later to update thr->coder->mem_in_use.
-	lzma_free(thr->in, thr->allocator);
-	thr->in = NULL;
-
 	mythread_sync(thr->mutex) {
+		// Block decoder ensures this, but do a sanity check anyway
+		// because thr->in_filled < thr->in_size means that the main
+		// thread is still writing to thr->in.
+		if (ret == LZMA_STREAM_END && thr->in_filled != thr->in_size) {
+			assert(0);
+			ret = LZMA_PROG_ERROR;
+		}
+
 		if (thr->state != THR_EXIT)
 			thr->state = THR_IDLE;
 	}
 
+	// Free the input buffer. Don't update in_size as we need
+	// it later to update thr->coder->mem_in_use.
+	//
+	// This step is skipped if an error occurred because the main thread
+	// might still be writing to thr->in. The memory will be freed after
+	// threads_end() sets thr->state = THR_EXIT.
+	if (ret == LZMA_STREAM_END) {
+		lzma_free(thr->in, thr->allocator);
+		thr->in = NULL;
+	}
+
 	mythread_sync(thr->coder->mutex) {
 		// Move our progress info to the main thread.
 		thr->coder->progress_in += thr->in_pos;
@@ -510,7 +486,20 @@
 				&& thr->coder->thread_error == LZMA_OK)
 			thr->coder->thread_error = ret;
 
-		worker_stop(thr);
+		// Return the worker thread to the stack of available
+		// threads only if no errors occurred.
+		if (ret == LZMA_STREAM_END) {
+			// Update memory usage counters.
+			thr->coder->mem_in_use -= thr->in_size;
+			thr->coder->mem_in_use -= thr->mem_filters;
+			thr->coder->mem_cached += thr->mem_filters;
+
+			// Put this thread to the stack of free threads.
+			thr->next = thr->coder->threads_free;
+			thr->coder->threads_free = thr;
+		}
+
+		mythread_cond_signal(&thr->coder->cond);
 	}
 
 	goto next_loop_lock;
@@ -544,17 +533,22 @@
 }
 
 
+/// Tell worker threads to stop without doing any cleaning up.
+/// The clean up will be done when threads_exit() is called;
+/// it's not possible to reuse the threads after threads_stop().
+///
+/// This is called before returning an unrecoverable error code
+/// to the application. It would be waste of processor time
+/// to keep the threads running in such a situation.
 static void
 threads_stop(struct lzma_stream_coder *coder)
 {
 	for (uint32_t i = 0; i < coder->threads_initialized; ++i) {
+		// The threads that are in the THR_RUN state will stop
+		// when they check the state the next time. There's no
+		// need to signal coder->threads[i].cond.
 		mythread_sync(coder->threads[i].mutex) {
-			// The state must be changed conditionally because
-			// THR_IDLE -> THR_STOP is not a valid state change.
-			if (coder->threads[i].state != THR_IDLE) {
-				coder->threads[i].state = THR_STOP;
-				mythread_cond_signal(&coder->threads[i].cond);
-			}
+			coder->threads[i].state = THR_IDLE;
 		}
 	}
 
@@ -1546,10 +1540,17 @@
 		// Read output from the output queue. Just like in
 		// SEQ_BLOCK_HEADER, we wait to fill the output buffer
 		// only if waiting_allowed was set to true in the beginning
-		// of this function (see the comment there).
+		// of this function (see the comment there) and there is
+		// no input available. In SEQ_BLOCK_HEADER, there is never
+		// input available when read_output_and_wait() is called,
+		// but here there can be when LZMA_FINISH is used, thus we
+		// need to check if *in_pos == in_size. Otherwise we would
+		// wait here instead of using the available input to start
+		// a new thread.
 		return_if_error(read_output_and_wait(coder, allocator,
 				out, out_pos, out_size,
-				NULL, waiting_allowed,
+				NULL,
+				waiting_allowed && *in_pos == in_size,
 				&wait_abs, &has_blocked));
 
 		if (coder->pending_error != LZMA_OK) {
@@ -1558,6 +1559,10 @@
 		}
 
 		// Return if the input didn't contain the whole Block.
+		//
+		// NOTE: When we updated coder->thr->in_filled a few lines
+		// above, the worker thread might by now have finished its
+		// work and returned itself back to the stack of free threads.
 		if (coder->thr->in_filled < coder->thr->in_size) {
 			assert(*in_pos == in_size);
 			return LZMA_OK;
@@ -1941,7 +1946,7 @@
 	// accounting from scratch, too. Changes in filter and block sizes may
 	// affect number of threads.
 	//
-	// FIXME? Reusing should be easy but unlike the single-threaded
+	// Reusing threads doesn't seem worth it. Unlike the single-threaded
 	// decoder, with some types of input file combinations reusing
 	// could leave quite a lot of memory allocated but unused (first
 	// file could allocate a lot, the next files could use fewer
diff --git a/configure b/configure
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.72 for XZ Utils 5.6.4.
+# Generated by GNU Autoconf 2.72 for XZ Utils 5.8.0.
 #
 # Report bugs to <lasse.collin@tukaani.org>.
 #
@@ -614,8 +614,8 @@
 # Identity of this package.
 PACKAGE_NAME='XZ Utils'
 PACKAGE_TARNAME='xz'
-PACKAGE_VERSION='5.6.4'
-PACKAGE_STRING='XZ Utils 5.6.4'
+PACKAGE_VERSION='5.8.0'
+PACKAGE_STRING='XZ Utils 5.8.0'
 PACKAGE_BUGREPORT='lasse.collin@tukaani.org'
 PACKAGE_URL='https://tukaani.org/xz/'
 
@@ -1523,7 +1523,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-'configure' configures XZ Utils 5.6.4 to adapt to many kinds of systems.
+'configure' configures XZ Utils 5.8.0 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1589,7 +1589,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of XZ Utils 5.6.4:";;
+     short | recursive ) echo "Configuration of XZ Utils 5.8.0:";;
    esac
   cat <<\_ACEOF
 
@@ -1794,7 +1794,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-XZ Utils configure 5.6.4
+XZ Utils configure 5.8.0
 generated by GNU Autoconf 2.72
 
 Copyright (C) 2023 Free Software Foundation, Inc.
@@ -2614,7 +2614,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by XZ Utils $as_me 5.6.4, which was
+It was created by XZ Utils $as_me 5.8.0, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -19175,7 +19175,54 @@
   prefix="${gt_save_prefix}"
 
 
+# The command line tools use UTF-8 on native Windows. Non-ASCII characters
+# display correctly only when using UCRT and gettext-runtime >= 0.23.1.
+case $USE_NLS-$host_os in #(
+  yes-mingw*) :
 
+		{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for UCRT and gettext-runtime >= 0.23.1" >&5
+printf %s "checking for UCRT and gettext-runtime >= 0.23.1... " >&6; }
+		cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+			#define WIN32_LEAN_AND_MEAN
+			#include <windows.h>
+			#include <libintl.h>
+
+			#ifndef _UCRT
+			#error "Not UCRT"
+			#endif
+
+			#if LIBINTL_VERSION < 0x001701
+			#error "gettext-runtime < 0.23.1"
+			#endif
+
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+			{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+
+else case e in #(
+  e)
+			{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+			as_fn_error $? "
+    Translation support (--enable-nls) on native Windows requires
+    UCRT and gettext-runtime >= 0.23.1. Use --disable-nls to build
+    with MSVCRT or old gettext-runtime." "$LINENO" 5
+		 ;;
+esac
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ ;; #(
+  *) :
+     ;;
+esac
+
+
 ###############################################################################
 # Checks for header files.
 ###############################################################################
@@ -19909,13 +19956,6 @@
 # __attribute__((__constructor__)) can be used for one-time initializations.
 # Use -Werror because some compilers accept unknown attributes and just
 # give a warning.
-#
-# FIXME? Unfortunately -Werror can cause trouble if CFLAGS contains options
-# that produce warnings for unrelated reasons. For example, GCC and Clang
-# support -Wunused-macros which will warn about "#define _GNU_SOURCE 1"
-# which will be among the #defines that Autoconf inserts to the beginning of
-# the test program. There seems to be no nice way to prevent Autoconf from
-# inserting the any defines to the test program.
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if __attribute__((__constructor__)) can be used" >&5
 printf %s "checking if __attribute__((__constructor__)) can be used... " >&6; }
 have_func_attribute_constructor=no
@@ -21696,10 +21736,10 @@
 
 			enable_sandbox=found
 
-			case $CFLAGS in #(
+			case "$CC $CFLAGS" in #(
   *-fsanitize=*) :
     as_fn_error $? "
-    CFLAGS contains '-fsanitize=' which is incompatible with the Landlock
+    CC or CFLAGS contain '-fsanitize=' which is incompatible with the Landlock
     sandboxing. Use --disable-sandbox when using '-fsanitize'." "$LINENO" 5 ;; #(
   *) :
      ;;
@@ -21891,6 +21931,8 @@
 			-Wmissing-prototypes \
 			-Wmissing-declarations \
 			-Wredundant-decls \
+			-Wimplicit-fallthrough \
+			-Wimplicit-fallthrough=5 \
 			\
 			-Wc99-compat \
 			-Wc11-extensions \
@@ -22745,7 +22787,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by XZ Utils $as_me 5.6.4, which was
+This file was extended by XZ Utils $as_me 5.8.0, which was
 generated by GNU Autoconf 2.72.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -22805,7 +22847,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-XZ Utils config.status 5.6.4
+XZ Utils config.status 5.8.0
 configured by $0, generated by GNU Autoconf 2.72,
   with options \\"\$ac_cs_config\\"
 
diff --git a/xz-clib.cabal b/xz-clib.cabal
--- a/xz-clib.cabal
+++ b/xz-clib.cabal
@@ -1,6 +1,6 @@
 cabal-version:       2.2
 name:                xz-clib
-version:             5.8.0.1
+version:             5.8.1
 
 synopsis:            LZMA/XZ clibs
 description:         C source code for the LZMA/XZ compression and decompression library
