mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Viacheslav Dubeyko <slava@dubeyko.com>
To: Matthias Goergens <matthias.goergens@gmail.com>
Cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>,
	Yangtao Li <frank.li@vivo.com>,
	linux-fsdevel@vger.kernel.org,  linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 2/2] hfsplus: validate the wrapper and partition map before following them
Date: Thu, 01 Oct 2026 14:18:41 -0700	[thread overview]
Message-ID: <1a1408e2ef7d3e5c27e520958ffa2116b3250ea6.camel@dubeyko.com> (raw)
In-Reply-To: <2037378c974c98794dcd1d832e3c31628e9801b8.1790689266.git.matthias.goergens@gmail.com>

On Fri, 2026-10-02 at 00:21 +0800, Matthias Goergens wrote:
> hfsplus_read_wrapper() rereads the volume header through a bare "goto
> reread", following an HFS wrapper's embedded-volume descriptor or, if
> the header matches neither signature, the partition map found by
> hfs_part_find().  hfsplus_read_mdb() does not check the offset it
> returns, and hfs_part_find() does not check the start of a new-style
> map
> entry, so a descriptor or entry with a zero offset leaves part_start
> where it was and the mount loops forever.
> 
> Check both where they are parsed.  TN1150 places the embedded volume
> in
> the wrapper's allocation blocks, and the boot blocks, MDB and volume
> bitmap of an HFS volume are not part of any allocation block.  So
> hfsplus_read_mdb() now rejects a wrapper whose allocation blocks
> start
> at or before its MDB (drAlBlSt <= 2), whose embedded volume is empty,
> or
> whose embedded volume ends past the wrapper's last allocation block
> (drNmAlBlks).  hfs_part_find() gets the same check as in hfs: a
> partition must be non-empty, start inside the device and, for a
> new-style map, start after the map, which begins at block 1 and whose
> size the first entry's pmMapBlkCnt gives (TN1189).
> 
> A wrapper hop now moves part_start forward by at least three sectors,
> and a partition-table hop moves it past the map entries it has read. 
> So
> the loop ends, at the latest when a read goes past the end of the
> device, and the maps read on the way do not overlap.
> 
> Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
> Cc: stable@vger.kernel.org
> Signed-off-by: Matthias Goergens <matthias.goergens@gmail.com>
> ---
> v2: check the wrapper in hfsplus_read_mdb() and the entries in
> hfs_part_find() instead of limiting the hops in
> hfsplus_read_wrapper().
> 
> A wrapper whose embedded-volume descriptor points back at itself
> hangs
> the mount without this patch and fails at once with it:
> 
>   img=hfsplus-wrapper-loop.img
>   put() { printf "$2" | dd of=$img bs=1 seek=$1 conv=notrunc
> status=none; }
>   truncate --size=64K $img
>   put $((1024 + 0x00)) '\x42\x44'          # drSigWord 'BD'
>   put $((1024 + 0x0a)) '\x82\x00'          # drAtrb: SLOCK | SPARED
>   put $((1024 + 0x14)) '\x00\x00\x02\x00'  # drAlBlkSiz 512
>   put $((1024 + 0x7c)) '\x48\x2b'          # drEmbedSigWord 'H+'
>   put $((1024 + 0x7e)) '\x00\x00\x00\x64'  # drEmbedExtent: start 0,
> count 100
>   mount -o ro,loop -t hfsplus $img /mnt
> 
> The partition map from the hfs patch hangs an hfsplus mount the same
> way, with "-t hfsplus".
> 
>  fs/hfsplus/part_tbl.c      | 24 +++++++++++++++++++++++-
>  fs/hfsplus/wrapper.c       | 16 +++++++++++++++-
>  include/linux/hfs_common.h |  1 +
>  3 files changed, 39 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/hfsplus/part_tbl.c b/fs/hfsplus/part_tbl.c
> index 9ec21664eda6b..ceba3139f1f56 100644
> --- a/fs/hfsplus/part_tbl.c
> +++ b/fs/hfsplus/part_tbl.c
> @@ -67,6 +67,22 @@ struct old_pmap {
>  	}	pdEntry[42];
>  } __packed;
>  
> +/*
> + * Check a partition map entry before following it.  The partition
> must
> + * be non-empty, start inside the device and start at or after
> @first:
> + * after the driver descriptor map in block 0 for an old-style map,
> and
> + * after the whole of a new-style map, whose size the first entry's
> + * pmMapBlkCnt gives (TN1189).  Every hop then moves forward, past
> the
> + * map entries just read, so hfsplus_read_wrapper() cannot loop and
> + * reads each block of a map at most once.
> + */
> +static bool hfs_part_valid(struct super_block *sb, sector_t base,
> +			   u64 first, u32 start, u32 size)
> +{
> +	return start >= first && size &&
> +	       base + start < bdev_nr_sectors(sb->s_bdev);
> +}

Please, see my comments for HFS patch. I have pretty the same comments
here.

> +
>  static int hfs_parse_old_pmap(struct super_block *sb, struct
> old_pmap *pm,
>  		sector_t *part_start, sector_t *part_size)
>  {
> @@ -76,7 +92,9 @@ static int hfs_parse_old_pmap(struct super_block
> *sb, struct old_pmap *pm,
>  	for (i = 0; i < 42; i++) {
>  		struct old_pmap_entry *p = &pm->pdEntry[i];
>  
> -		if (p->pdStart && p->pdSize &&
> +		if (hfs_part_valid(sb, *part_start, HFS_DD_BLK + 1,
> +				   be32_to_cpu(p->pdStart),
> +				   be32_to_cpu(p->pdSize)) &&
>  		    p->pdFSID == cpu_to_be32(0x54465331)/*"TFS1"*/
> &&
>  		    (sbi->part < 0 || sbi->part == i)) {
>  			*part_start += be32_to_cpu(p->pdStart);
> @@ -99,6 +117,10 @@ static int hfs_parse_new_pmap(struct super_block
> *sb, void *buf,
>  
>  	do {
>  		if (!memcmp(pm->pmPartType, "Apple_HFS", 9) &&
> +		    hfs_part_valid(sb, *part_start,
> +				   HFS_PMAP_BLK + (u64)size,
> +				   be32_to_cpu(pm->pmPyPartStart),
> +				   be32_to_cpu(pm->pmPartBlkCnt)) &&
>  		    (sbi->part < 0 || sbi->part == i)) {
>  			*part_start += be32_to_cpu(pm-
> >pmPyPartStart);
>  			*part_size = be32_to_cpu(pm->pmPartBlkCnt);
> diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c
> index 30cf4fe78b3d2..35cd2563dfdfe 100644
> --- a/fs/hfsplus/wrapper.c
> +++ b/fs/hfsplus/wrapper.c
> @@ -66,7 +66,7 @@ int hfsplus_submit_bio(struct super_block *sb,
> sector_t sector,
>  static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
>  {
>  	u32 extent;
> -	u16 attrib;
> +	u16 attrib, nmalblks;
>  	__be16 sig;
>  
>  	sig = *(__be16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG);
> @@ -87,11 +87,25 @@ static int hfsplus_read_mdb(void *bufptr, struct
> hfsplus_wd *wd)
>  		return 0;
>  	wd->ablk_start =
>  		be16_to_cpu(*(__be16 *)(bufptr +
> HFSP_WRAPOFF_ABLKSTART));
> +	nmalblks = be16_to_cpu(*(__be16 *)(bufptr +
> HFSP_WRAPOFF_NMALBLKS));
>  
>  	extent = get_unaligned_be32(bufptr + HFSP_WRAPOFF_EMBEDEXT);
>  	wd->embed_start = (extent >> 16) & 0xFFFF;
>  	wd->embed_count = extent & 0xFFFF;
>  
> +	/*
> +	 * The boot blocks, MDB and volume bitmap of an HFS volume
> are not
> +	 * part of any allocation block, and the embedded volume
> occupies
> +	 * allocation blocks of the wrapper (TN1150).  So the
> embedded
> +	 * volume starts after the wrapper's MDB and ends inside the
> +	 * wrapper.
> +	 */
> +	if (wd->ablk_start <= HFS_MDB_BLK)
> +		return 0;
> +	if (!wd->embed_count ||
> +	    wd->embed_start + wd->embed_count > nmalblks)
> +		return 0;
> +
>  	return 1;
>  }
>  
> diff --git a/include/linux/hfs_common.h b/include/linux/hfs_common.h
> index d6a615e74b26e..123475efe5b37 100644
> --- a/include/linux/hfs_common.h
> +++ b/include/linux/hfs_common.h
> @@ -44,6 +44,7 @@
>  
>  #define HFSP_WRAPOFF_SIG		0x00
>  #define HFSP_WRAPOFF_ATTRIB		0x0A
> +#define HFSP_WRAPOFF_NMALBLKS		0x12

Does this value is based on specification? Where does it come from?

Thanks,
Slava.

>  #define HFSP_WRAPOFF_ABLKSIZE		0x14
>  #define HFSP_WRAPOFF_ABLKSTART		0x1C
>  #define HFSP_WRAPOFF_EMBEDSIG		0x7C

      reply	other threads:[~2026-10-01 21:18 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-01 16:21 [PATCH v2 0/2] hfs, hfsplus: validate the partition map and wrapper " Matthias Goergens
2026-10-01 16:21 ` [PATCH v2 1/2] hfs: validate partition map entries in hfs_part_find() Matthias Goergens
2026-10-01 21:15   ` Viacheslav Dubeyko
2026-10-01 16:21 ` [PATCH v2 2/2] hfsplus: validate the wrapper and partition map before following them Matthias Goergens
2026-10-01 21:18   ` Viacheslav Dubeyko [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1a1408e2ef7d3e5c27e520958ffa2116b3250ea6.camel@dubeyko.com \
    --to=slava@dubeyko.com \
    --cc=frank.li@vivo.com \
    --cc=glaubitz@physik.fu-berlin.de \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=matthias.goergens@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®