git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jeff King <peff@peff.net>
To: "René Scharfe" <l.s.r@web.de>
Cc: Junio C Hamano <gitster@pobox.com>, Git List <git@vger.kernel.org>
Subject: free and errno, was Re: [PATCH] apply: replace mksnpath() with a mkpathdup() call
Date: Thu, 4 Apr 2024 18:53:13 -0400	[thread overview]
Message-ID: <20240404225313.GA2512966@coredump.intra.peff.net> (raw)
In-Reply-To: <df774306-f29b-4a75-a282-59db89812b9a@web.de>

On Thu, Apr 04, 2024 at 11:08:38PM +0200, René Scharfe wrote:

> Support paths longer than PATH_MAX in create_one_file() (which is not a
> hard limit e.g. on Linux) by calling mkpathdup() instead of mksnpath().
> Remove the latter, as it becomes unused by this change.  Resist the
> temptation of using the more convenient mkpath() to avoid introducing a
> dependency on a static variable deep inside the apply machinery.
> 
> Suggested-by: Jeff King <peff@peff.net>
> Signed-off-by: René Scharfe <l.s.r@web.de>

Heh, so obviously I had the same patch stewing. But one thing that gave
me pause is: do we need to worry about preserving errno across free()
boundaries?

Traditionally touching errno was something free() was allowed to do, and
there were definitely cases where glibc would do so (mostly due to
munmap). But recent versions of POSIX clarify that it should not touch
errno, and glibc as of 2.33 does not (which dates to 2021).

This issue from 2015 talks about "the next version of POSIX" making that
change:

  https://sourceware.org/bugzilla/show_bug.cgi?id=17924

but it looks to me from:

  https://www.austingroupbugs.net/view.php?id=385

that the change wasn't accepted there until 2020 (and AFAICT that hasn't
resulted in a new published spec yet).

Now it would be pretty darn useful to not have to worry about preserving
errno. It's subtle code that's hard to remember is needed, and sometimes
hard to get right depending on the rest of the flow control.

Years like "2020" and "2021" are too recent for us to say "oh, that's
ancient history, we don't have to care anymore". But I wonder if we can
be a little cavalier here for two reasons:

  - it's rare; for the most part free() isn't going to touch errno.
    Failures from munmap() are pretty rare, and small allocations like
    this are probably done with sbrk() anyway. Of course that's just
    talking about glibc, and there are other platforms. But it still
    seems like it should be a rarity for any free() implementation to
    fail or to want to touch errno.

  - the stakes are usually pretty low; the outcome in most cases would
    be a misleading error message as we clobber errno. But note that it
    does sometimes affect control flow (this patch is an example; we are
    checking for EEXIST to break out of the loop).

So would it be that unreasonable to assume the modern, desired behavior,
and mostly shrug our shoulders for other platforms? We could even
provide:

  #ifdef FREE_CLOBBERS_ERRNO
  void git_free(void *ptr)
  {
        int saved_errno = errno;
        free(ptr);
        errno = saved_errno;
  }
  #define free(ptr) git_free(ptr)
  #endif

if we really wanted to be careful, though it's hard to know which
platforms even need it (and again, it's so rare to come up in practice
that I'd suspect people could go for a long time before realizing their
platform was a problem). I guess the flip side is that we could use the
function above by default, and disable it selectively (the advantage of
disabling it being that it's slightly more efficient; maybe that's not
even measurable?).

>  		for (;;) {
> -			char newpath[PATH_MAX];
> -			mksnpath(newpath, sizeof(newpath), "%s~%u", path, nr);
> +			char *newpath = mkpathdup("%s~%u", path, nr);
>  			res = try_create_file(state, newpath, mode, buf, size);
> -			if (res < 0)
> +			if (res < 0) {
> +				free(newpath);
>  				return -1;
> +			}
>  			if (!res) {
> -				if (!rename(newpath, path))
> +				if (!rename(newpath, path)) {
> +					free(newpath);
>  					return 0;
> +				}
>  				unlink_or_warn(newpath);
> +				free(newpath);
>  				break;
>  			}
> +			free(newpath);
>  			if (errno != EEXIST)
>  				break;
>  			++nr;

At any rate, you can probably see the places where free() clobbering
errno would be a problem here. Our return when "res < 0" (though I don't
think any of the callers actually care about errno after that), the
check for EEXIST at the bottom of the loop, and after we break out of
the loop, we use error_errno() to report it.

-Peff

  parent reply	other threads:[~2024-04-04 22:53 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-04-04 21:08 [PATCH] apply: replace mksnpath() with a mkpathdup() call René Scharfe
2024-04-04 21:29 ` Junio C Hamano
2024-04-04 22:53 ` Jeff King [this message]
2024-04-04 23:08   ` free and errno, was " Junio C Hamano
2024-04-05 10:52   ` René Scharfe
2024-04-05 17:35     ` Jeff King
2024-04-05 17:41       ` Jeff King
2024-04-06 17:45       ` René Scharfe
2024-04-07  1:18         ` Jeff King
2024-04-14 15:17           ` René Scharfe
2024-04-24  1:11             ` Jeff King
2024-04-05 10:53 ` [PATCH v2 1/2] apply: avoid fixed-size buffer in create_one_file() René Scharfe
2024-04-05 10:56   ` [PATCH v2 2/2] path: remove mksnpath() René Scharfe
2024-04-05 17:37     ` Jeff King
2024-04-05 16:51   ` [PATCH v2 1/2] apply: avoid fixed-size buffer in create_one_file() Junio C Hamano

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=20240404225313.GA2512966@coredump.intra.peff.net \
    --to=peff@peff.net \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=l.s.r@web.de \
    /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;
as well as URLs for NNTP newsgroup(s).