NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/52993: case sensitive HFS+ can return data from wrong file
>Number: 52993
>Category: kern
>Synopsis: case sensitive HFS+ can return data from wrong file
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Thu Feb 08 11:10:00 +0000 2018
>Originator: Harold Gutch
>Release: NetBSD 7.1 (but the bug exists from 5.0 to HEAD)
>Organization:
>Environment:
NetBSD 7.1 NetBSD 7.1 (GENERIC.201703111743Z) amd64
>Description:
On a case sensitive HFS+ file system, when locating a file of a given name, file names are compared against it as memcmp(name1, name2, min(len(name1), len(name2))). Thus, if name1 starts with name2 (or vice versa) they are understood to be identical and as a result when accessing name1 we might end up reading from name2 instead.
The issue does not occur for case insensitive HFS+ file systems (the default).
>How-To-Repeat:
1) On Mac OS, create a case sensitive HFS+ file system, create two files one's name starts with the other's, and fill them with different data.
osx:tmp hg$ hdiutil create test.dmg -size 1m -fs HFS+X -volname "test"
...............................................................................
created: /Users/hg/tmp/test.dmg
osx:tmp hg$ hdiutil mount test.dmg
/dev/disk7 GUID_partition_scheme
/dev/disk7s1 Apple_HFS /Volumes/test
osx:tmp hg$ echo foo > /Volumes/test/test
osx:tmp hg$ echo bar > /Volumes/test/test123
osx:tmp hg$ cat /Volumes/test/test
foo
osx:tmp hg$ cat /Volumes/test/test123
bar
2) After umounting, copy the .dmg over to NetBSD. There mount and check the inode numbers and the contents of the files.
ichiban# vndconfig vnd0 test.dmg
ichiban# gpt show vnd0 | grep HFS
40 1968 1 GPT part - Apple HFS
ichiban# dkctl vnd0 addwedge test 40 1968 hfs
dk0 created successfully.
ichiban# mount -t hfs -o ro /dev/dk0 /mnt
ichiban# cat /mnt/test
foo
ichiban# cat /mnt/test123
foo
ichiban# ls -li /mnt/test123 /mnt/test
22 -rw-r--r-- 1 503 staff 4 Feb 6 14:47 /mnt/test
22 -rw-r--r-- 1 503 staff 4 Feb 6 14:47 /mnt/test123
3) Notice that on NetBSD both, the contents of test and test123, and also their inode number are identical.
>Fix:
[ NOTE: the actual fix is in the few lines after "/* FIXME: ...", the rest is merely a bit of syntax sugar without functional change that just avoids the repeated casts to (const hfs_catalog_key_t*) all over the function and the same then again in hfslib_compare_extent_keys(). ]
--- src/sys/fs/hfs/libhfs.c.orig 2015-06-21 13:40:25.000000000 +0000
+++ src/sys/fs/hfs/libhfs.c 2018-02-06 11:51:04.000000000 +0000
@@ -2384,37 +2384,42 @@
/* binary compare (i.e., not case folding) */
int
hfslib_compare_catalog_keys_bc (
- const void *a,
- const void *b)
+ const void *ap,
+ const void *bp)
{
- if (((const hfs_catalog_key_t*)a)->parent_cnid
- == ((const hfs_catalog_key_t*)b)->parent_cnid)
+ int c;
+ const hfs_catalog_key_t *a, *b;
+
+ a = (const hfs_catalog_key_t *) ap;
+ b = (const hfs_catalog_key_t *) bp;
+
+ if (a->parent_cnid == b->parent_cnid)
{
- if (((const hfs_catalog_key_t*)a)->name.length == 0 &&
- ((const hfs_catalog_key_t*)b)->name.length == 0)
+ if (a->name.length == 0 && b->name.length == 0)
return 0;
- if (((const hfs_catalog_key_t*)a)->name.length == 0)
+ if (a->name.length == 0)
return -1;
- if (((const hfs_catalog_key_t*)b)->name.length == 0)
+ if (b->name.length == 0)
return 1;
/* FIXME: This does a byte-per-byte comparison, whereas the HFS spec
* mandates a uint16_t chunk comparison. */
- return memcmp(((const hfs_catalog_key_t*)a)->name.unicode,
- ((const hfs_catalog_key_t*)b)->name.unicode,
- min(((const hfs_catalog_key_t*)a)->name.length,
- ((const hfs_catalog_key_t*)b)->name.length));
+ c = memcmp(a->name.unicode, b->name.unicode,
+ sizeof(unichar_t)*min(a->name.length, b->name.length));
+ if (c != 0)
+ return c;
+ else
+ return (a->name.length - b->name.length);
} else {
- return (((const hfs_catalog_key_t*)a)->parent_cnid -
- ((const hfs_catalog_key_t*)b)->parent_cnid);
+ return (a->parent_cnid - b->parent_cnid);
}
}
int
hfslib_compare_extent_keys (
- const void *a,
- const void *b)
+ const void *ap,
+ const void *bp)
{
/*
* Comparison order, in descending importance:
@@ -2422,27 +2427,25 @@
* CNID -> fork type -> start block
*/
- if (((const hfs_extent_key_t*)a)->file_cnid
- == ((const hfs_extent_key_t*)b)->file_cnid)
+ const hfs_extent_key_t *a, *b;
+ a = (const hfs_extent_key_t *) ap;
+ b = (const hfs_extent_key_t *) bp;
+
+ if (a->file_cnid == b->file_cnid)
{
- if (((const hfs_extent_key_t*)a)->fork_type
- == ((const hfs_extent_key_t*)b)->fork_type)
+ if (a->fork_type == b->fork_type)
{
- if (((const hfs_extent_key_t*)a)->start_block
- == ((const hfs_extent_key_t*)b)->start_block)
+ if (a->start_block == b->start_block)
{
return 0;
} else {
- return (((const hfs_extent_key_t*)a)->start_block -
- ((const hfs_extent_key_t*)b)->start_block);
+ return (a->start_block - b->start_block);
}
} else {
- return (((const hfs_extent_key_t*)a)->fork_type -
- ((const hfs_extent_key_t*)b)->fork_type);
+ return (a->fork_type - b->fork_type);
}
} else {
- return (((const hfs_extent_key_t*)a)->file_cnid -
- ((const hfs_extent_key_t*)b)->file_cnid);
+ return (a->file_cnid - b->file_cnid);
}
}
Home |
Main Index |
Thread Index |
Old Index