Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 26 Aug 2017 08:49:12 +1000 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        Conrad Meyer <cem@freebsd.org>
Cc:        Alan Somers <asomers@freebsd.org>,  src-committers <src-committers@freebsd.org>,  "svn-src-all@freebsd.org" <svn-src-all@freebsd.org>,  "svn-src-head@freebsd.org" <svn-src-head@freebsd.org>
Subject:   Re: svn commit: r322893 - head/bin/dd
Message-ID:  <20170826075540.X976@besplex.bde.org>
In-Reply-To: <CAG6CVpVf8TNqnErZzoofuqy-TA9cXoZgf2W8Y2J2vNa63Vmu7Q@mail.gmail.com>
References:  <201708251531.v7PFVtoZ038242@repo.freebsd.org> <CAG6CVpXmL95eAjf4rXTL8QqnLcu5aQPRZ4cwk3sLGUSR35jnBg@mail.gmail.com> <CAOtMX2gXBcQyxTr_KivoGoKBP6UXL-sq9KhhB89fBgESfWV0yg@mail.gmail.com> <CAG6CVpVf8TNqnErZzoofuqy-TA9cXoZgf2W8Y2J2vNa63Vmu7Q@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Fri, 25 Aug 2017, Conrad Meyer wrote:

> Well, not negative, just large uint64_t numbers that would be negative
> as off_t (int64_t).
>
> E.g., dd if=/dev/kmem bs=1 iseek=0xfffff...foo count=8.  I think we
> would like that to work.  I don't recall whether it does or not before
> this change.

This is broken on 64-bit systems, first by the horrible get_off_t(),
then by broken range checking in at least old versions of dd.

The first bug in get_off_t() is that it uses strtoimax().  This fails
for offsets >= INTMAX_MAX.  This breaks the natural use of dd to seek
to the kernel part of kmem using dd.  E.g.:

     dd if=/dev/kmem bs=1 count=1 iseek=0xffffffff802d2100

This should give a negative offset than then works.  (lseek() to negative
offsets is implementation-defined for special files, and FreeBSD defines
it so that it works for seeking in kmem.  This depends on some 2's complement
magic to represent large unsigned offsets as negative signed.)

However, it doesn't work to double bs and halve iseek.  The multiplication
is then done by a higher level.  It exceeds OFF_MAX, so the lseek() isn't
tried.

get_off_t() but not the higher level is fixed in my version.

It does work to calculate the negative offset in another way inside
get_off_t().  E.g., bs=1 iseek=-1 gives -1 which is passed to lseek().
bs=2 iseek=-1 also works to give the correct offset of -2 for lseek().
The arithmetic for converting large number like 0xffffffff802d2100 to
a negative value is painful, especially since shells have similar bugs
near INTMAX_MAX and UINTMAX_MAX and with signed and/or unsigned types
even if they support 64-bit integers.

The buggy range checking for bs=2 iseek=0x7fffffff802d2100 seems to be
only in old versions of dd.  It was in jcl(), and was just for the
initial args (multiplying them would exceed OFF_MAX).  Now there seems
to be no check for overflow.  The multiplication in pos_in() just
overflows even if the check in jcl() passed, later when the seek point
advances past INTMAX_MAX (if this exceeds OFF_MAX, then passing the
result of the multiplicating to lseek() starts overflowing before the
multiplication does).

[Context lost to top posting].

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20170826075540.X976>