From: Petr Mladek <pmladek@suse.com>
To: John Ogness <john.ogness@linutronix.de>
Cc: "feng.zhou" <realsummitzhou@gmail.com>,
linux-kernel@vger.kernel.org, senozhatsky@chromium.org,
rostedt@goodmis.org
Subject: Re: [PATCH] printk: Fix _DESCS_COUNT type for 64-bit systems
Date: Tue, 3 Mar 2026 17:52:34 +0100 [thread overview]
Message-ID: <aacR0i4zLBM4M5oq@pathway.suse.cz> (raw)
In-Reply-To: <87o6m7fk4c.fsf@jogness.linutronix.de>
On Mon 2026-02-02 12:47:23, John Ogness wrote:
> Hi Zhou,
>
> Thanks for reporting and fixing this!
>
> On 2026-02-02, "feng.zhou" <realsummitzhou@gmail.com> wrote:
> > The _DESCS_COUNT macro currently uses 1U (32-bit unsigned) instead of
> > 1UL (unsigned long), which breaks the intended overflow testing design
> > on 64-bit systems.
> >
> > Problem Analysis:
> > ----------------
> > The printk_ringbuffer uses a deliberate design choice to initialize
> > descriptor IDs near the maximum 62-bit value to trigger overflow early
> > in the system's lifetime. This is documented in printk_ringbuffer.h:
> >
> > "initial values are chosen that map to the correct initial array
> > indexes, but will result in overflows soon."
> >
> > The DESC0_ID macro calculates:
> > DESC0_ID(ct_bits) = DESC_ID(-(_DESCS_COUNT(ct_bits) + 1))
> >
> > On 64-bit systems with typical configuration (descbits=16):
> > - Current buggy behavior: DESC0_ID = 0xfffeffff
> > - Expected behavior: DESC0_ID = 0x3ffffffffffeffff
> >
> > The buggy version only uses 32 bits, which means:
> > 1. The initial ID is nowhere near 2^62
> > 2. It would take ~140 trillion wraps to trigger 62-bit overflow
> > 3. The overflow handling code is never tested in practice
> >
> > Root Cause:
> > ----------
> > The issue is in this line:
> > #define _DESCS_COUNT(ct_bits) (1U << (ct_bits))
> >
> > When _DESCS_COUNT(16) is calculated:
> > 1U << 16 = 0x10000 (32-bit value)
> > -(0x10000 + 1) = -0x10001 = 0xFFFEFFFF (32-bit two's complement)
> >
> > On 64-bit systems, this 32-bit value doesn't get extended to create
> > the intended 62-bit ID near the maximum value.
>
> I was surprised to see that 1U is used here. I did some historical
> digging and it has existed since the very first private version of this
> ringbuffer implementation. It was a private email to Petr:
Yeah.
> Date: 12 Oct 2019
> Subject: ringbuffer v1
> Message-ID: <87lftqwvhp.fsf@linutronix.de>
>
> That was the very first appearance of a descriptor initializer:
>
> +#define _DESCS_COUNT(ct_bits) (1U << (ct_bits))
> +#define DESCS_COUNT(dr) _DESCS_COUNT((dr)->count_bits)
> +#define DESC0_ID(ct_bits) DESC_ID(-_DESCS_COUNT(ct_bits))
>
> > Impact:
> > ------
> > While index calculations still work correctly in the short term, this
> > bug has several implications:
> >
> > 1. Violates the design intention documented in the code
> > 2. Overflow handling code paths remain untested
> > 3. ABA detection code doesn't get exercised under overflow conditions
>
> The ABA detection is only relevant for 32-bit systems. The patch is only
> fixing an issue on 64-bit systems.
>
> > 4. In extreme long-term running scenarios (though unlikely), could
> > potentially cause issues when ID actually reaches 2^62
>
> It is worth investigating if this is an issue. Although it is quite
> unlikely. A machine would need to generate 100k printk messages per
> second for 490k years!
Yes, I think that it is not much realistic.
> > Verification:
> > ------------
> > Tested on ARM64 system with CONFIG_LOG_BUF_SHIFT=20 (descbits=15):
> > - Before fix: DESC0_ID(16) = 0xfffeffff
> > - After fix: DESC0_ID(16) = 0x3fffffffffff7fff
> >
> > The fix aligns _DESCS_COUNT with _DATA_SIZE, which already correctly
> > uses 1UL:
> > #define _DATA_SIZE(sz_bits) (1UL << (sz_bits))
> >
> > Signed-off-by: feng.zhou <realsummitzhou@gmail.com>
> > ---
> > kernel/printk/printk_ringbuffer.h | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/kernel/printk/printk_ringbuffer.h b/kernel/printk/printk_ringbuffer.h
> > index 4ef81349d9fb..4f4949700676 100644
> > --- a/kernel/printk/printk_ringbuffer.h
> > +++ b/kernel/printk/printk_ringbuffer.h
> > @@ -122,7 +122,7 @@ enum desc_state {
> > };
> >
> > #define _DATA_SIZE(sz_bits) (1UL << (sz_bits))
> > -#define _DESCS_COUNT(ct_bits) (1U << (ct_bits))
> > +#define _DESCS_COUNT(ct_bits) (1UL << (ct_bits))
> > #define DESC_SV_BITS BITS_PER_LONG
> > #define DESC_FLAGS_SHIFT (DESC_SV_BITS - 2)
> > #define DESC_FLAGS_MASK (3UL << DESC_FLAGS_SHIFT)
>
> The change looks correct. But I would like to take some more time to
> understand why I never used UL in the first place.
Good question.
Anyway, my opinion about this patch evolved over time:
1. My first feeling was that this patch was not worth the effort
and risk. As explained above, we would start testing corner-cases
which could never be reached in reality.
2. Then I started looking at the code and realized that _DESCS_COUNT() was
used also to initialize:
#define DESCS_COUNT(desc_ring) _DESCS_COUNT((desc_ring)->count_bits)
#define DESCS_COUNT_MASK(desc_ring) (DESCS_COUNT(desc_ring) - 1)
and it would be great to be sure that _MASK covers the whole 64-bit
unsigned long
=> patch was worth it.
Also the @id is primary used to create index to @desc_ring using
DESC_INDEX(). And I am sure that rotation of the @desc_ring is
tested heavily
=> we should be on the safe side.
Also I did some testing with the patch and did not find any problem.
3. Finally, I decided to double check how @id is handled in the code.
And it is stored with state bits in @state_var
=> the highest 2 bits are cleared by DESC_ID() macro
=> ringing bells
And indeed desc_reserve() shrinks the highest two bits
when computing the next @id:
id = DESC_ID(head_id + 1);
and the masked @id is stored:
} while (!atomic_long_try_cmpxchg(&desc_ring->head_id, &head_id,
id)); /* LMM(desc_reserve:D) */
Now, get_desc_state() does a check:
static enum desc_state get_desc_state(unsigned long id,
unsigned long state_val)
{
if (id != DESC_ID(state_val))
return desc_miss;
[...]
}
I belive that this check might fail when DESC_ID(state_val)
is compared against non-masked @id
But we seems to be on the safe side, because DESC0 is defined
using DESC_ID() => the highest bits are masked:
#define DESC0_ID(ct_bits) DESC_ID(-(_DESCS_COUNT(ct_bits) + 1))
=> we seem to be on the safe side.
Conclusion:
The patch looks correct and acceptable to me:
Reviewed-by: Petr Mladek <pmladek@suse.com>
Tested-by: Petr Mladek <pmladek@suse.com>
I am going to wait a bit whether John agrees with my opinion
or whether he remembers and finds any blocker.
> Also note (unrelated) that we are using 1U for the buffer of the data
> ring. Not really an issue, but we should probably switch that to UL as
> well.
I might make sense to change this as well. But I would do it
in a separate patch.
We should be on the safe size even now because the log buffer size
is limited by 2GB, see
#define LOG_BUF_LEN_MAX ((u32)1 << 31)
Best Regards,
Petr
next prev parent reply other threads:[~2026-03-03 16:52 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-02 9:41 feng.zhou
2026-02-02 11:41 ` John Ogness
2026-02-02 15:07 ` [RESEND] " feng.zhou
2026-02-03 17:24 ` Petr Mladek
2026-03-03 16:52 ` Petr Mladek [this message]
2026-03-10 16:37 ` Petr Mladek
2026-03-10 21:40 ` John Ogness
2026-03-12 9:02 ` Petr Mladek
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=aacR0i4zLBM4M5oq@pathway.suse.cz \
--to=pmladek@suse.com \
--cc=john.ogness@linutronix.de \
--cc=linux-kernel@vger.kernel.org \
--cc=realsummitzhou@gmail.com \
--cc=rostedt@goodmis.org \
--cc=senozhatsky@chromium.org \
/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®