From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-lf1-x133.google.com (mail-lf1-x133.google.com [IPv6:2a00:1450:4864:20::133]) by sourceware.org (Postfix) with ESMTPS id 8796B3864A32 for ; Wed, 7 Dec 2022 08:18:04 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 8796B3864A32 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-lf1-x133.google.com with SMTP id d6so27495978lfs.10 for ; Wed, 07 Dec 2022 00:18:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=Cuhj/safYpsW8G1eMRCze+wjYvexcuUJhcTdu9fJOiY=; b=dEv0qZf5QbTiJIA9LCU6O6ugOZM8Ia0/dLQVtrITLFTkIMERHr3dhkbTjM1AWfaHGD JByLOEtjEAFPEiJmd6H783puupZUq5vBIiZX5WaY7IuzdHB2pXXTjuDtMpvrzXsMUMhJ 0GforvyVAsNvsj0iVzUxI9RBM2BMQ4OUMj//QwvkMs/otBWFiDHTgHgkSj07C1ANSta/ on/00U7mMubZPUJ724zL07Qc+9TMXMfTDzvhsFur0L7FcakpsuRpCkme4rOt/90qbM2z zOcEKqsaPID84KIINNTFDT8/iKGmeIbeEeuq6KXkL/P7MXEjk8npecofinFtdgw4QGiy SrqQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Cuhj/safYpsW8G1eMRCze+wjYvexcuUJhcTdu9fJOiY=; b=NY2jlcsP1CFjYH3swPs98zRXlBmDkfTONH4+jv5WwI5sLqmYaQuQafpMv9JHzlCCRF apDT7HnZPl+8xm+0DHsGBwNdo82GI/krNIpU/IU4lohMVw2i2rD1LtPK5MZ4EUGvcpqn GrUTId1RSvT3veKFlN4EZvmOAPG6re/sfOtPCKYP0gDeXIf5Ju96JiDPIKHW6LorykfK aWgOuVeSb6YjlHAxpiE7OK4ySks5lqpIq2m781ADM7B87Wd4vjpCBbo0XM9WmO1OuFFk CcIbVdzbEPIM13QwVkEAEO115mKuiLJvv9/mnj1JpMAZJQ4rouzBVSKUFl33PLYeHMDj 741A== X-Gm-Message-State: ANoB5pkKF3s6V7qGn1NptXdlhA2xzs4levJskEelvjjYPR2efISK1EId K/dmW98kqZMMC7KEXvfTJJh4V51xid6ThusoqGI= X-Google-Smtp-Source: AA0mqf7ODWA8idBBdYzqs3ip/gLSBzArOIrP0UL5r9UcI3B8opT6ehZxSZLOMOQAKhPkI9miU1dU/6EV4mpQBIRoaF0= X-Received: by 2002:a05:6512:398f:b0:4b1:39c2:c675 with SMTP id j15-20020a056512398f00b004b139c2c675mr28488608lfu.448.1670401082501; Wed, 07 Dec 2022 00:18:02 -0800 (PST) MIME-Version: 1.0 References: <55aaeff6-11ad-b7e5-1e81-bee6edc606e4@gmail.com> In-Reply-To: <55aaeff6-11ad-b7e5-1e81-bee6edc606e4@gmail.com> From: Richard Biener Date: Wed, 7 Dec 2022 09:17:50 +0100 Message-ID: Subject: Re: Spurious warning for zero-sized array parameters to a function To: Alejandro Colomar , Martin Sebor Cc: gcc@gcc.gnu.org Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-0.6 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,KAM_ASCII_DIVIDERS,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On Tue, Dec 6, 2022 at 5:22 PM Alejandro Colomar via Gcc wrote: > > Hi! > > In the following function, past_end is a pointer to one-past-the-end of t= he > array. Holding such a pointer is legal in C. I use it as a sentinel val= ue that > helps (1) avoid overrunning the buffer, and (2) detect truncation. I mar= k it as > having a size of [0], which clearly states that it can't be dereferenced = (and as > you can see, I don't). > > /* > * This function copies an unterminated string into a string. > * - It never overruns the dest buffer. > * - It can be chained, to concatenate strings. > * - It detects truncation. > * - Truncation only needs to be tested once after all concatenations. > * - The name is self-documenting, compared to its alternative: strncat= (3). > */ > char * > ustr2stpe(char *dst, const char *restrict src, size_t n, char past_end[0]= ) > { > bool trunc; > char *end; > ptrdiff_t len; > > if (dst =3D=3D past_end) > return past_end; > > trunc =3D false; > len =3D strnlen(src, n); > if (len > past_end - dst - 1) { > len =3D past_end - dst - 1; > trunc =3D true; > } > > end =3D mempcpy(dst, src, len); > *end =3D '\0'; > > return trunc ? past_end : end; > } > > > If I compile the code above, GCC considers the function definition to be = fine. > However, at call site, it always warns: > > > #define nitems(arr) (sizeof((arr)) / sizeof((arr)[0])) > > int > main(void) > { > char pre[4] =3D "pre."; > char *post =3D ".post"; > char *src =3D "some-long-body.post"; > char dest[100]; > char *p, *past_end; > > past_end =3D dest + nitems(dest); > p =3D dest; > p =3D ustr2stpe(p, pre, nitems(pre), past_end); > p =3D ustr2stpe(p, src, strlen(src) - strlen(post), past_end); > p =3D ustr2stpe(p, "", 0, past_end); > if (p =3D=3D past_end) > fprintf(stderr, "truncation\n"); > > puts(dest); // "pre.some-long-body" > } > > > > $ cc -Wall -Wextra ustr2stpe.c > ustr2stpe.c: In function =E2=80=98main=E2=80=99: > ustr2stpe.c:43:13: warning: =E2=80=98ustr2stpe=E2=80=99 accessing 1 byte = in a region of size 0 > [-Wstringop-overflow=3D] > 43 | p =3D ustr2stpe(p, pre, nitems(pre), past_end); > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > ustr2stpe.c:43:13: note: referencing argument 4 of type =E2=80=98char[0]= =E2=80=99 > ustr2stpe.c:10:1: note: in a call to function =E2=80=98ustr2stpe=E2=80=99 > 10 | ustr2stpe(char *dst, const char *restrict src, size_t n, char pa= st_end[0]) > | ^~~~~~~~~ > ustr2stpe.c:44:13: warning: =E2=80=98ustr2stpe=E2=80=99 accessing 1 byte = in a region of size 0 > [-Wstringop-overflow=3D] > 44 | p =3D ustr2stpe(p, src, strlen(src) - strlen(post), past= _end); > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~= ~~~ > ustr2stpe.c:44:13: note: referencing argument 4 of type =E2=80=98char[0]= =E2=80=99 > ustr2stpe.c:10:1: note: in a call to function =E2=80=98ustr2stpe=E2=80=99 > 10 | ustr2stpe(char *dst, const char *restrict src, size_t n, char pa= st_end[0]) > | ^~~~~~~~~ > ustr2stpe.c:45:13: warning: =E2=80=98ustr2stpe=E2=80=99 accessing 1 byte = in a region of size 0 > [-Wstringop-overflow=3D] > 45 | p =3D ustr2stpe(p, "", 0, past_end); > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > ustr2stpe.c:45:13: note: referencing argument 4 of type =E2=80=98char[0]= =E2=80=99 > ustr2stpe.c:10:1: note: in a call to function =E2=80=98ustr2stpe=E2=80=99 > 10 | ustr2stpe(char *dst, const char *restrict src, size_t n, char pa= st_end[0]) > | ^~~~~~~~~ > ustr2stpe.c:43:13: warning: =E2=80=98ustr2stpe=E2=80=99 accessing 1 byte = in a region of size 0 > [-Wstringop-overflow=3D] > 43 | p =3D ustr2stpe(p, pre, nitems(pre), past_end); > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > ustr2stpe.c:43:13: note: referencing argument 4 of type =E2=80=98char[0]= =E2=80=99 > ustr2stpe.c:10:1: note: in a call to function =E2=80=98ustr2stpe=E2=80=99 > 10 | ustr2stpe(char *dst, const char *restrict src, size_t n, char pa= st_end[0]) > | ^~~~~~~~~ > ustr2stpe.c:44:13: warning: =E2=80=98ustr2stpe=E2=80=99 accessing 1 byte = in a region of size 0 > [-Wstringop-overflow=3D] > 44 | p =3D ustr2stpe(p, src, strlen(src) - strlen(post), past= _end); > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~= ~~~ > ustr2stpe.c:44:13: note: referencing argument 4 of type =E2=80=98char[0]= =E2=80=99 > ustr2stpe.c:10:1: note: in a call to function =E2=80=98ustr2stpe=E2=80=99 > 10 | ustr2stpe(char *dst, const char *restrict src, size_t n, char pa= st_end[0]) > | ^~~~~~~~~ > ustr2stpe.c:45:13: warning: =E2=80=98ustr2stpe=E2=80=99 accessing 1 byte = in a region of size 0 > [-Wstringop-overflow=3D] > 45 | p =3D ustr2stpe(p, "", 0, past_end); > | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ > ustr2stpe.c:45:13: note: referencing argument 4 of type =E2=80=98char[0]= =E2=80=99 > ustr2stpe.c:10:1: note: in a call to function =E2=80=98ustr2stpe=E2=80=99 > 10 | ustr2stpe(char *dst, const char *restrict src, size_t n, char pa= st_end[0]) > | ^~~~~~~~~ > > > The warnings are invalid. While it's true that I'm referencing a pointer= of > size 0, it's false that I'm "accessing 1 byte" in that region. I guess t= his is > all about the bogus design of 'static' in ISO C, where you can have an ar= ray > parameter of size 0, which is very useful in cases like this one. It looks like we run into pass_waccess::maybe_check_access_sizes doing if (sizidx =3D=3D -1) { /* If only the pointer attribute operand was specified and not size, set SIZE to the greater of MINSIZE or size of one element of the pointed to type to detect smaller objects (null pointers are diagnosed in this case only if the pointer is also declared with attribute nonnull. */ if (access.second.minsize && access.second.minsize !=3D HOST_WIDE_INT_M1U) access_nelts =3D build_int_cstu (sizetype, access.second.minsiz= e); else if (VOID_TYPE_P (argtype) && access.second.mode =3D=3D acces= s_none) /* Treat access mode none on a void* argument as expecting as little as zero bytes. */ access_nelts =3D size_zero_node; else access_nelts =3D size_one_node; and use size_one_node as fallback - it either doesn't consider [0] "valid" = or for some reason chooses to interpret it as "unknown". Can you file a bugre= port please? Martin? Richard. > > Cheers, > > Alex > > > -- >