* [PATCH AUTOSEL 6.18-6.6] fs/ntfs3: fix KMSAN uninit-value in ni_create_attr_list
[not found] <20251209001610.611575-1-sashal@kernel.org>
@ 2025-12-09 0:14 ` Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.15] fs/ntfs3: Support timestamps prior to epoch Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.6] ntfs: set dummy blocksize to read boot_block when mounting Sasha Levin
2 siblings, 0 replies; 4+ messages in thread
From: Sasha Levin @ 2025-12-09 0:14 UTC (permalink / raw)
To: patches, stable
Cc: Nirbhay Sharma, syzbot+83c9dd5c0dcf6184fdbf, Konstantin Komarov,
Sasha Levin, ntfs3
From: Nirbhay Sharma <nirbhay.lkd@gmail.com>
[ Upstream commit 5f33da04e6ceee849e76e6592cc283c72fef7af9 ]
The call to kmalloc() to allocate the attribute list buffer is given a
size of al_aligned(rs). This size can be larger than the data
subsequently copied into the buffer, leaving trailing bytes uninitialized.
This can trigger a KMSAN "uninit-value" warning if that memory is
later accessed.
Fix this by using kzalloc() instead, which ensures the entire
allocated buffer is zero-initialized, preventing the warning.
Reported-by: syzbot+83c9dd5c0dcf6184fdbf@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=83c9dd5c0dcf6184fdbf
Signed-off-by: Nirbhay Sharma <nirbhay.lkd@gmail.com>
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
Now I have a complete understanding. Let me summarize my analysis:
---
## Detailed Analysis
### 1. COMMIT MESSAGE ANALYSIS
**Subject**: `fs/ntfs3: fix KMSAN uninit-value in ni_create_attr_list`
**Keywords identified**:
- "fix" - clearly a bug fix
- "KMSAN uninit-value" - refers to Kernel Memory Sanitizer detecting
uninitialized memory access
**Tags present**:
- `Reported-by: syzbot+83c9dd5c0dcf6184fdbf@syzkaller.appspotmail.com` -
bug was found by automated fuzzer syzkaller
- `Closes: https://syzkaller.appspot.com/bug?extid=83c9dd5c0dcf6184fdbf`
- links to original bug report
- `Signed-off-by`: Two sign-offs from the author and ntfs3 maintainer
**Missing tags**:
- No `Cc: stable@vger.kernel.org` tag (though this doesn't preclude
backporting)
- No `Fixes:` tag pointing to the original buggy commit
### 2. CODE CHANGE ANALYSIS
**The Bug**: In `ni_create_attr_list()` at line 770:
```c
le = kmalloc(al_aligned(rs), GFP_NOFS);
```
**Problem mechanism**:
1. `al_aligned(rs)` rounds up `rs` (record_size, typically 1024 or 4096
bytes) to the nearest 1024-byte boundary: `(size + 1023) & ~1023`
2. The allocated buffer can be larger than the actual data populated
into it
3. The loop copies attribute list entries into the buffer, and the
actual used size is computed as `lsize = PtrOffset(ni->attr_list.le,
le)`
4. The trailing bytes between `lsize` and `al_aligned(rs)` remain
uninitialized
5. KMSAN detects when these uninitialized bytes are later accessed (even
for comparison checks)
**The Fix**:
```c
le = kzalloc(al_aligned(rs), GFP_NOFS);
```
This changes to `kzalloc()` which zero-initializes the entire buffer,
eliminating any uninitialized memory concerns.
**Why it works**: Zero-initialization ensures all bytes in the allocated
buffer have known values, preventing KMSAN warnings even if the unused
trailing bytes are accessed during boundary checks or other operations.
### 3. CLASSIFICATION
- **Bug fix**: Yes, this fixes a real bug (KMSAN uninit-value warning)
- **Device ID/quirk**: No
- **Build fix**: No
- **Security**: Not directly a security vulnerability, but uninitialized
memory issues can sometimes have security implications (information
disclosure)
### 4. SCOPE AND RISK ASSESSMENT
**Lines changed**: 1 line
**Files touched**: 1 file (`fs/ntfs3/frecord.c`)
**Complexity**: Minimal - simple `kmalloc` → `kzalloc` change
**Subsystem**: NTFS3 filesystem driver
- This is a relatively mature filesystem driver (introduced in v5.15)
- Used for reading/writing NTFS-formatted storage devices
**Risk assessment**: **VERY LOW**
- The change only adds initialization, cannot introduce new bugs
- `kzalloc()` is a standard, well-tested kernel API
- Zero-initialization is always safe - at worst it's slightly slower
(negligible for this use case)
- No functional changes to the logic
### 5. USER IMPACT
**Who is affected**:
- Any user mounting NTFS filesystems
- Operations that create attribute lists (complex NTFS files with many
attributes)
**Severity of original bug**:
- KMSAN warnings indicate potential undefined behavior
- While this may not cause crashes in production (KMSAN is typically
only enabled in test builds), it indicates a code quality issue
- Could potentially cause intermittent data corruption if the
uninitialized data is ever written to disk (though the code appears to
bound writes by `attr_list.size`)
**Likelihood**:
- Medium - attribute list creation is a common operation in NTFS
- The bug was found by syzkaller fuzzing, indicating it's triggerable
### 6. STABILITY INDICATORS
- **Reported-by**: syzkaller (automated fuzzer with good track record)
- **Maintainer Sign-off**: Yes, signed off by Konstantin Komarov (NTFS3
maintainer)
- Commit has been in mainline since v6.18-rc1
### 7. DEPENDENCY CHECK
- **Dependencies**: None - this is a self-contained one-line fix
- **Code exists in stable trees**: Yes, `ni_create_attr_list()` and the
`kmalloc()` call exist in all stable kernels since v5.15 (when ntfs3
was introduced)
- **Clean backport**: Should apply cleanly to any kernel 5.15+
### Summary
This commit is an excellent candidate for stable backporting because:
1. **Fixes a real bug**: KMSAN uninit-value warning triggered by
syzkaller
2. **Minimal and surgical**: Single-line change from `kmalloc` to
`kzalloc`
3. **Obviously correct**: Zero-initialization is always safe
4. **Zero risk**: Cannot introduce regressions - only adds
initialization
5. **Reported by automated testing**: syzkaller found this, indicating
it's triggerable
6. **Maintainer approved**: Signed off by ntfs3 maintainer
7. **Applicable to all stable trees**: The buggy code exists since ntfs3
was introduced in v5.15
8. **No dependencies**: Completely self-contained fix
The only minor concern is the lack of an explicit `Cc: stable` tag and
`Fixes:` tag, but this is not a hard requirement. The commit clearly
fixes a bug in filesystem code that affects users mounting NTFS drives.
**YES**
fs/ntfs3/frecord.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/fs/ntfs3/frecord.c b/fs/ntfs3/frecord.c
index 8f9fe1d7a6908..4fe8da7fc0343 100644
--- a/fs/ntfs3/frecord.c
+++ b/fs/ntfs3/frecord.c
@@ -767,7 +767,7 @@ int ni_create_attr_list(struct ntfs_inode *ni)
* Skip estimating exact memory requirement.
* Looks like one record_size is always enough.
*/
- le = kmalloc(al_aligned(rs), GFP_NOFS);
+ le = kzalloc(al_aligned(rs), GFP_NOFS);
if (!le)
return -ENOMEM;
--
2.51.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH AUTOSEL 6.18-5.15] fs/ntfs3: Support timestamps prior to epoch
[not found] <20251209001610.611575-1-sashal@kernel.org>
2025-12-09 0:14 ` [PATCH AUTOSEL 6.18-6.6] fs/ntfs3: fix KMSAN uninit-value in ni_create_attr_list Sasha Levin
@ 2025-12-09 0:15 ` Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-6.6] ntfs: set dummy blocksize to read boot_block when mounting Sasha Levin
2 siblings, 0 replies; 4+ messages in thread
From: Sasha Levin @ 2025-12-09 0:15 UTC (permalink / raw)
To: patches, stable; +Cc: Konstantin Komarov, Sasha Levin, ntfs3
From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
[ Upstream commit 5180138604323895b5c291eca6aa7c20be494ade ]
Before it used an unsigned 64-bit type, which prevented proper handling
of timestamps earlier than 1970-01-01. Switch to a signed 64-bit type to
support pre-epoch timestamps. The issue was caught by xfstests.
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
Now I have a complete understanding. Let me write the detailed analysis:
## Detailed Analysis
### 1. COMMIT MESSAGE ANALYSIS
**Subject:** `fs/ntfs3: Support timestamps prior to epoch`
**Commit Body:**
- The commit explains that before this fix, the code used an **unsigned
64-bit type** (`u64`), which prevented proper handling of timestamps
earlier than 1970-01-01 (the Unix epoch).
- The fix switches to a **signed 64-bit type** (`s64`) to support pre-
epoch timestamps.
- The issue was caught by **xfstests test 258**, which specifically
tests for timestamps prior to the epoch.
**Tags:**
- **No `Cc: stable@vger.kernel.org` tag** is present.
- **No `Fixes:` tag** is present.
### 2. CODE CHANGE ANALYSIS
#### The Bug:
In the current code (before the patch):
```980:987:fs/ntfs3/ntfs_fs.h
static inline void nt2kernel(const __le64 tm, struct timespec64 *ts)
{
u64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
// WARNING: do_div changes its first argument(!)
ts->tv_nsec = do_div(t, _100ns2seconds) * 100;
ts->tv_sec = t;
}
```
The variable `t` is declared as `u64` (unsigned 64-bit integer). NTFS
timestamps are stored as the number of 100-nanosecond intervals since
January 1, 1601. After subtracting `SecondsToStartOf1970`
(0x00000002B6109100), which represents the number of seconds between
1601 and 1970, `t` represents a Unix timestamp.
**Problem:** When the NTFS timestamp represents a date before January 1,
1970, the subtraction produces a **negative** result. But since `t` is
unsigned (`u64`), the negative value wraps around to a very large
positive value, resulting in a wrong (future) timestamp.
**The `do_div` macro** operates on unsigned integers and uses unsigned
division, which doesn't handle negative values correctly.
#### The Fix:
The patch changes:
1. `u64 t` → `s64 t` (signed 64-bit integer)
2. `do_div(t, _100ns2seconds)` → `div_s64_rem(t, _100ns2seconds, &t32)`
(signed division)
3. Introduces `s32 t32` to hold the remainder
The new code:
```c
static inline void nt2kernel(const __le64 tm, struct timespec64 *ts)
{
s32 t32;
/* use signed 64 bit to support timestamps prior to epoch.
xfstest 258. */
s64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
ts->tv_sec = div_s64_rem(t, _100ns2seconds, &t32);
ts->tv_nsec = t32 * 100;
}
```
`div_s64_rem()` is designed for signed 64-bit division with a 32-bit
divisor, properly handling negative values and returning the remainder
through a pointer.
**Root Cause:** Using unsigned arithmetic for a value that can be
negative (pre-epoch timestamps).
### 3. CLASSIFICATION
- **Bug fix:** YES - This is fixing incorrect behavior with timestamps
before 1970.
- **New feature:** NO - It doesn't add new functionality, only fixes
existing functionality.
- **Security fix:** NO - No security implications.
- **Exception category:** NO - Not a device ID, quirk, DT update, build
fix, or documentation fix.
### 4. SCOPE AND RISK ASSESSMENT
**Lines Changed:** Very minimal - changes ~8 lines of code within a
single inline function.
**Files Touched:** 1 file (`fs/ntfs3/ntfs_fs.h`)
**Subsystem Complexity:**
- NTFS3 is a relatively new filesystem (added in 5.15)
- The change is in a core time conversion function `nt2kernel()` that is
called whenever reading timestamps from disk
- All file timestamps (creation, modification, access times) use this
function
**Risk Assessment:**
- **LOW RISK** - The change is small, surgical, and uses a well-
established kernel API (`div_s64_rem`)
- The new code is mathematically equivalent to the old code for
timestamps after 1970
- For timestamps before 1970, the new code produces correct results
(negative `tv_sec` values)
- The `timespec64` structure's `tv_sec` field is `time64_t` which is
signed (`__s64`), so it can properly represent negative timestamps
### 5. USER IMPACT
**Who is affected?**
- Users of NTFS3 filesystem
- Users with NTFS volumes containing files with pre-1970 timestamps
**How severe?**
- The bug causes **data corruption** (incorrect timestamp
display/storage) for pre-epoch files
- Without this fix, timestamps like January 1, 1960 would appear as some
date far in the future
- This breaks file management tools and any software relying on correct
timestamps
**Test Coverage:**
- The fix was identified by **xfstests generic/258**, a standard
filesystem test that specifically validates pre-epoch timestamp
handling
- This is a well-known and commonly run test suite
### 6. STABILITY INDICATORS
- **No `Tested-by:` tag** - However, xfstests is mentioned, implying the
fix was tested against this test suite
- **No `Reviewed-by:` tag**
- The commit is from the ntfs3 maintainer (Konstantin Komarov from
Paragon Software)
### 7. DEPENDENCY CHECK
- **No dependencies** - This is a standalone fix
- Uses existing kernel APIs (`div_s64_rem`, `s64`, `s32`) that have been
available for a long time
- The affected code (`nt2kernel` function) has existed since ntfs3 was
introduced in kernel 5.15
### Summary
**YES signals:**
1. ✅ Fixes a **real bug** (incorrect timestamps for pre-epoch dates)
2. ✅ **Small and surgical** change - only 8 lines changed in one
function
3. ✅ **Obviously correct** - uses proper signed arithmetic for values
that can be negative
4. ✅ No new features, no new APIs
5. ✅ Caught by **xfstests**, a standard fs test suite (test 258)
6. ✅ Applies to all stable kernels that have ntfs3 (5.15+)
7. ✅ Zero risk of regression for post-epoch timestamps (mathematically
equivalent)
8. ✅ Fixes **data integrity issue** (timestamp corruption)
**NO signals:**
1. ⚠️ No explicit `Cc: stable@vger.kernel.org` tag
2. ⚠️ No `Fixes:` tag
3. ⚠️ No `Tested-by:` or `Reviewed-by:` tags
**Verdict:**
This is a clear bug fix that addresses a real issue (incorrect pre-epoch
timestamp handling) that affects NTFS3 users. The fix is small,
surgical, uses well-tested kernel APIs, and is mathematically correct.
While the commit lacks stable tags, the nature of the fix - correcting
data integrity issues with timestamps - makes it appropriate for
backporting. The bug has existed since ntfs3 was introduced (5.15), so
all stable trees with ntfs3 would benefit from this fix.
The lack of `Cc: stable` tag is likely an oversight since the fix
clearly meets stable kernel criteria: it's a bug fix, small in scope,
obviously correct, and fixes a real user-visible issue (incorrect file
timestamps).
**YES**
fs/ntfs3/ntfs_fs.h | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index 630128716ea73..2649fbe16669d 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -979,11 +979,12 @@ static inline __le64 kernel2nt(const struct timespec64 *ts)
*/
static inline void nt2kernel(const __le64 tm, struct timespec64 *ts)
{
- u64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
+ s32 t32;
+ /* use signed 64 bit to support timestamps prior to epoch. xfstest 258. */
+ s64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
- // WARNING: do_div changes its first argument(!)
- ts->tv_nsec = do_div(t, _100ns2seconds) * 100;
- ts->tv_sec = t;
+ ts->tv_sec = div_s64_rem(t, _100ns2seconds, &t32);
+ ts->tv_nsec = t32 * 100;
}
static inline struct ntfs_sb_info *ntfs_sb(struct super_block *sb)
--
2.51.0
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH AUTOSEL 6.18-6.6] ntfs: set dummy blocksize to read boot_block when mounting
[not found] <20251209001610.611575-1-sashal@kernel.org>
2025-12-09 0:14 ` [PATCH AUTOSEL 6.18-6.6] fs/ntfs3: fix KMSAN uninit-value in ni_create_attr_list Sasha Levin
2025-12-09 0:15 ` [PATCH AUTOSEL 6.18-5.15] fs/ntfs3: Support timestamps prior to epoch Sasha Levin
@ 2025-12-09 0:15 ` Sasha Levin
2 siblings, 0 replies; 4+ messages in thread
From: Sasha Levin @ 2025-12-09 0:15 UTC (permalink / raw)
To: patches, stable
Cc: Pedro Demarchi Gomes, syzbot+f4f84b57a01d6b8364ad,
Konstantin Komarov, Sasha Levin, ntfs3
From: Pedro Demarchi Gomes <pedrodemargomes@gmail.com>
[ Upstream commit d1693a7d5a38acf6424235a6070bcf5b186a360d ]
When mounting, sb->s_blocksize is used to read the boot_block without
being defined or validated. Set a dummy blocksize before attempting to
read the boot_block.
The issue can be triggered with the following syz reproducer:
mkdirat(0xffffffffffffff9c, &(0x7f0000000080)='./file1\x00', 0x0)
r4 = openat$nullb(0xffffffffffffff9c, &(0x7f0000000040), 0x121403, 0x0)
ioctl$FS_IOC_SETFLAGS(r4, 0x40081271, &(0x7f0000000980)=0x4000)
mount(&(0x7f0000000140)=@nullb, &(0x7f0000000040)='./cgroup\x00',
&(0x7f0000000000)='ntfs3\x00', 0x2208004, 0x0)
syz_clone(0x88200200, 0x0, 0x0, 0x0, 0x0, 0x0)
Here, the ioctl sets the bdev block size to 16384. During mount,
get_tree_bdev_flags() calls sb_set_blocksize(sb, block_size(bdev)),
but since block_size(bdev) > PAGE_SIZE, sb_set_blocksize() leaves
sb->s_blocksize at zero.
Later, ntfs_init_from_boot() attempts to read the boot_block while
sb->s_blocksize is still zero, which triggers the bug.
Reported-by: syzbot+f4f84b57a01d6b8364ad@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=f4f84b57a01d6b8364ad
Signed-off-by: Pedro Demarchi Gomes <pedrodemargomes@gmail.com>
[almaz.alexandrovich@paragon-software.com: changed comment style, added
return value handling]
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
Now I have a complete picture of the issue. Let me summarize my
analysis:
## Summary of Analysis
### The Bug
The commit fixes a bug in ntfs3's `ntfs_init_from_boot()` function where
`ntfs_bread()` is called to read the boot block **before**
`sb->s_blocksize` is properly set.
### Root Cause Analysis
1. **Mount Flow**: When mounting an ntfs3 filesystem:
- `get_tree_bdev()` calls `setup_bdev_super()` which calls
`sb_set_blocksize(sb, block_size(bdev))`
- If `block_size(bdev) > PAGE_SIZE` (which can happen via ioctl
`FS_IOC_SETFLAGS` setting bdev block size to 16384 as shown in the
reproducer), and the filesystem doesn't have `FS_LBS` flag,
`sb_set_blocksize()` returns 0 and leaves `sb->s_blocksize = 0`
2. **The Crash Path**: In `ntfs_init_from_boot()`:
```c
sbi->volume.blocks = dev_size >> PAGE_SHIFT;
// sb->s_blocksize is 0 here!
bh = ntfs_bread(sb, boot_block); // Uses sb->s_blocksize = 0
```
3. **Cascade to Infinite Loop**:
- `ntfs_bread()` → `sb_bread_unmovable()` → `__bread_gfp()` with
size=0
- `bdev_getblk()` → `__getblk_slow()` → `grow_buffers()` →
`grow_dev_folio()` → `folio_alloc_buffers()`
- In `folio_alloc_buffers()`: `while ((offset -= size) >= 0)` with
size=0 causes infinite loop
### The Fix
The fix adds a call to `sb_min_blocksize(sb, PAGE_SIZE)` before
attempting to read the boot block:
```c
/* Set dummy blocksize to read boot_block. */
if (!sb_min_blocksize(sb, PAGE_SIZE)) {
return -EINVAL;
}
```
This ensures:
1. `sb->s_blocksize` is set to at least the device's logical block size,
capped at PAGE_SIZE
2. If this fails, mount fails gracefully with `-EINVAL` instead of
hanging
### Backport Assessment
**STRONG YES signals:**
1. ✅ **Fixes a real crash/hang** - System hangs due to infinite loop in
`folio_alloc_buffers()`
2. ✅ **Syzbot reported** - Has syzkaller reproducer
(`f4f84b57a01d6b8364ad`)
3. ✅ **Small, surgical fix** - Only 4 lines added
4. ✅ **Clear, obvious fix** - Sets blocksize before using it for reads
5. ✅ **Uses well-established API** - `sb_min_blocksize()` is a standard
helper used by many filesystems
6. ✅ **Affects production users** - ntfs3 is widely used (included since
5.15)
7. ✅ **Denial of Service potential** - A local user can trigger the hang
**Risk Assessment:**
- **Very Low Risk**: The fix adds a safety check before an I/O operation
- **No behavioral change** for normal cases - the blocksize would have
been set to PAGE_SIZE anyway after successful boot read
- **Graceful failure** if `sb_min_blocksize()` fails (which would be
very rare in practice)
**Stable Trees Affected:**
- All stable trees with ntfs3: 5.15.y, 6.1.y, 6.6.y, 6.10.y, 6.11.y,
etc.
- The bug has existed since ntfs3 was introduced in v5.15
- The recent `FS_LBS` change (March 2025) made it easier to trigger but
the underlying issue predates it
**Dependencies:**
- The fix is standalone and uses existing kernel APIs
- No other patches required for backport
- `sb_min_blocksize()` has been available since at least 2.6.x
**YES**
fs/ntfs3/super.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/fs/ntfs3/super.c b/fs/ntfs3/super.c
index ddff94c091b8c..e6c0908e27c29 100644
--- a/fs/ntfs3/super.c
+++ b/fs/ntfs3/super.c
@@ -933,6 +933,11 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,
sbi->volume.blocks = dev_size >> PAGE_SHIFT;
+ /* Set dummy blocksize to read boot_block. */
+ if (!sb_min_blocksize(sb, PAGE_SIZE)) {
+ return -EINVAL;
+ }
+
read_boot:
bh = ntfs_bread(sb, boot_block);
if (!bh)
--
2.51.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH AUTOSEL 6.18-5.15] fs/ntfs3: Support timestamps prior to epoch
[not found] <20251206140252.645973-1-sashal@kernel.org>
@ 2025-12-06 14:02 ` Sasha Levin
0 siblings, 0 replies; 4+ messages in thread
From: Sasha Levin @ 2025-12-06 14:02 UTC (permalink / raw)
To: patches, stable; +Cc: Konstantin Komarov, Sasha Levin, ntfs3
From: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
[ Upstream commit 5180138604323895b5c291eca6aa7c20be494ade ]
Before it used an unsigned 64-bit type, which prevented proper handling
of timestamps earlier than 1970-01-01. Switch to a signed 64-bit type to
support pre-epoch timestamps. The issue was caught by xfstests.
Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
Now I have a complete understanding. Let me write the detailed analysis:
## Detailed Analysis
### 1. COMMIT MESSAGE ANALYSIS
**Subject:** `fs/ntfs3: Support timestamps prior to epoch`
**Commit Body:**
- The commit explains that before this fix, the code used an **unsigned
64-bit type** (`u64`), which prevented proper handling of timestamps
earlier than 1970-01-01 (the Unix epoch).
- The fix switches to a **signed 64-bit type** (`s64`) to support pre-
epoch timestamps.
- The issue was caught by **xfstests test 258**, which specifically
tests for timestamps prior to the epoch.
**Tags:**
- **No `Cc: stable@vger.kernel.org` tag** is present.
- **No `Fixes:` tag** is present.
### 2. CODE CHANGE ANALYSIS
#### The Bug:
In the current code (before the patch):
```980:987:fs/ntfs3/ntfs_fs.h
static inline void nt2kernel(const __le64 tm, struct timespec64 *ts)
{
u64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
// WARNING: do_div changes its first argument(!)
ts->tv_nsec = do_div(t, _100ns2seconds) * 100;
ts->tv_sec = t;
}
```
The variable `t` is declared as `u64` (unsigned 64-bit integer). NTFS
timestamps are stored as the number of 100-nanosecond intervals since
January 1, 1601. After subtracting `SecondsToStartOf1970`
(0x00000002B6109100), which represents the number of seconds between
1601 and 1970, `t` represents a Unix timestamp.
**Problem:** When the NTFS timestamp represents a date before January 1,
1970, the subtraction produces a **negative** result. But since `t` is
unsigned (`u64`), the negative value wraps around to a very large
positive value, resulting in a wrong (future) timestamp.
**The `do_div` macro** operates on unsigned integers and uses unsigned
division, which doesn't handle negative values correctly.
#### The Fix:
The patch changes:
1. `u64 t` → `s64 t` (signed 64-bit integer)
2. `do_div(t, _100ns2seconds)` → `div_s64_rem(t, _100ns2seconds, &t32)`
(signed division)
3. Introduces `s32 t32` to hold the remainder
The new code:
```c
static inline void nt2kernel(const __le64 tm, struct timespec64 *ts)
{
s32 t32;
/* use signed 64 bit to support timestamps prior to epoch.
xfstest 258. */
s64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
ts->tv_sec = div_s64_rem(t, _100ns2seconds, &t32);
ts->tv_nsec = t32 * 100;
}
```
`div_s64_rem()` is designed for signed 64-bit division with a 32-bit
divisor, properly handling negative values and returning the remainder
through a pointer.
**Root Cause:** Using unsigned arithmetic for a value that can be
negative (pre-epoch timestamps).
### 3. CLASSIFICATION
- **Bug fix:** YES - This is fixing incorrect behavior with timestamps
before 1970.
- **New feature:** NO - It doesn't add new functionality, only fixes
existing functionality.
- **Security fix:** NO - No security implications.
- **Exception category:** NO - Not a device ID, quirk, DT update, build
fix, or documentation fix.
### 4. SCOPE AND RISK ASSESSMENT
**Lines Changed:** Very minimal - changes ~8 lines of code within a
single inline function.
**Files Touched:** 1 file (`fs/ntfs3/ntfs_fs.h`)
**Subsystem Complexity:**
- NTFS3 is a relatively new filesystem (added in 5.15)
- The change is in a core time conversion function `nt2kernel()` that is
called whenever reading timestamps from disk
- All file timestamps (creation, modification, access times) use this
function
**Risk Assessment:**
- **LOW RISK** - The change is small, surgical, and uses a well-
established kernel API (`div_s64_rem`)
- The new code is mathematically equivalent to the old code for
timestamps after 1970
- For timestamps before 1970, the new code produces correct results
(negative `tv_sec` values)
- The `timespec64` structure's `tv_sec` field is `time64_t` which is
signed (`__s64`), so it can properly represent negative timestamps
### 5. USER IMPACT
**Who is affected?**
- Users of NTFS3 filesystem
- Users with NTFS volumes containing files with pre-1970 timestamps
**How severe?**
- The bug causes **data corruption** (incorrect timestamp
display/storage) for pre-epoch files
- Without this fix, timestamps like January 1, 1960 would appear as some
date far in the future
- This breaks file management tools and any software relying on correct
timestamps
**Test Coverage:**
- The fix was identified by **xfstests generic/258**, a standard
filesystem test that specifically validates pre-epoch timestamp
handling
- This is a well-known and commonly run test suite
### 6. STABILITY INDICATORS
- **No `Tested-by:` tag** - However, xfstests is mentioned, implying the
fix was tested against this test suite
- **No `Reviewed-by:` tag**
- The commit is from the ntfs3 maintainer (Konstantin Komarov from
Paragon Software)
### 7. DEPENDENCY CHECK
- **No dependencies** - This is a standalone fix
- Uses existing kernel APIs (`div_s64_rem`, `s64`, `s32`) that have been
available for a long time
- The affected code (`nt2kernel` function) has existed since ntfs3 was
introduced in kernel 5.15
### Summary
**YES signals:**
1. ✅ Fixes a **real bug** (incorrect timestamps for pre-epoch dates)
2. ✅ **Small and surgical** change - only 8 lines changed in one
function
3. ✅ **Obviously correct** - uses proper signed arithmetic for values
that can be negative
4. ✅ No new features, no new APIs
5. ✅ Caught by **xfstests**, a standard fs test suite (test 258)
6. ✅ Applies to all stable kernels that have ntfs3 (5.15+)
7. ✅ Zero risk of regression for post-epoch timestamps (mathematically
equivalent)
8. ✅ Fixes **data integrity issue** (timestamp corruption)
**NO signals:**
1. ⚠️ No explicit `Cc: stable@vger.kernel.org` tag
2. ⚠️ No `Fixes:` tag
3. ⚠️ No `Tested-by:` or `Reviewed-by:` tags
**Verdict:**
This is a clear bug fix that addresses a real issue (incorrect pre-epoch
timestamp handling) that affects NTFS3 users. The fix is small,
surgical, uses well-tested kernel APIs, and is mathematically correct.
While the commit lacks stable tags, the nature of the fix - correcting
data integrity issues with timestamps - makes it appropriate for
backporting. The bug has existed since ntfs3 was introduced (5.15), so
all stable trees with ntfs3 would benefit from this fix.
The lack of `Cc: stable` tag is likely an oversight since the fix
clearly meets stable kernel criteria: it's a bug fix, small in scope,
obviously correct, and fixes a real user-visible issue (incorrect file
timestamps).
**YES**
fs/ntfs3/ntfs_fs.h | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/fs/ntfs3/ntfs_fs.h b/fs/ntfs3/ntfs_fs.h
index 630128716ea73..2649fbe16669d 100644
--- a/fs/ntfs3/ntfs_fs.h
+++ b/fs/ntfs3/ntfs_fs.h
@@ -979,11 +979,12 @@ static inline __le64 kernel2nt(const struct timespec64 *ts)
*/
static inline void nt2kernel(const __le64 tm, struct timespec64 *ts)
{
- u64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
+ s32 t32;
+ /* use signed 64 bit to support timestamps prior to epoch. xfstest 258. */
+ s64 t = le64_to_cpu(tm) - _100ns2seconds * SecondsToStartOf1970;
- // WARNING: do_div changes its first argument(!)
- ts->tv_nsec = do_div(t, _100ns2seconds) * 100;
- ts->tv_sec = t;
+ ts->tv_sec = div_s64_rem(t, _100ns2seconds, &t32);
+ ts->tv_nsec = t32 * 100;
}
static inline struct ntfs_sb_info *ntfs_sb(struct super_block *sb)
--
2.51.0
^ permalink raw reply related [flat|nested] 4+ messages in thread