Thanks for reporting the bug; I installed the attached somewhat-fancier patch 
to fix it.

Thanks also for the test case; I sent you separately instructions for the 
copyright paperwork.
From cfdb891bbd68c920ea75210977593f3cd08f0418 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Tue, 13 Oct 2026 08:56:40 -0700
Subject: [PATCH] tar: fix two-digit octal escapes

Problem reported by Nexory in:
https://lists.gnu.org/r/bug-tar/2026-10/msg00005.html
* src/common.h (is_octal_digit): Make inline and move here ...
* src/list.c: ... from here.
* src/misc.c (unquote_string): Simplify code and fix
octal-escape handling.
---
 src/common.h |  6 ++++++
 src/list.c   |  6 ------
 src/misc.c   | 17 ++++++-----------
 3 files changed, 12 insertions(+), 17 deletions(-)

diff --git a/src/common.h b/src/common.h
index 38f9958e..cbb7906d 100644
--- a/src/common.h
+++ b/src/common.h
@@ -494,6 +494,12 @@ void buffer_write_global_xheader (void);
 const char *first_decompress_program (int *pstate);
 const char *next_decompress_program (int *pstate);
 
+COMMON_INLINE bool
+is_octal_digit (char c)
+{
+  return '0' <= c && c <= '7';
+}
+
 /* Sum values returned by printf to estimate the total bytes output.
    Estimate -1 if there was a problem, e.g., int overflow or I/O error.  */
 COMMON_INLINE intmax_t
diff --git a/src/list.c b/src/list.c
index b0da0cec..6d4dc7ae 100644
--- a/src/list.c
+++ b/src/list.c
@@ -630,12 +630,6 @@ read_header (union block **return_block, struct tar_stat_info *info,
   return status;
 }
 
-static bool
-is_octal_digit (char c)
-{
-  return '0' <= c && c <= '7';
-}
-
 /* Decode things from a file HEADER block into STAT_INFO, also setting
    *FORMAT_POINTER depending on the header block format.  If
    DO_USER_GROUP, decode the user/group information (this is useful
diff --git a/src/misc.c b/src/misc.c
index 499ea3e1..6135fff9 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -244,19 +244,14 @@ unquote_string (char *string)
 	case '7':
 	  {
 	    unsigned char value = *source++ - '0';
-
-	    if (*source < '0' || *source > '7')
+	    if (is_octal_digit (*source))
 	      {
-		*destination++ = value;
-		break;
-	      }
-	    unsigned char val1 = value * 8 + (*source++ - '0'), val2;
-	    if (*source < '0' || *source > '7' || ckd_mul (&val2, val1, 8))
-	      {
-		*destination++ = value;
-		break;
+		value = value * 8 + (*source++ - '0');
+		unsigned char val;
+		if (is_octal_digit (*source) && !ckd_mul (&val, value, 8))
+		  value = val + (*source++ - '0');
 	      }
-	    *destination++ = val2 + (*source++ - '0');
+	    *destination++ = value;
 	    break;
 	  }
 
-- 
2.53.0

Reply via email to