# safe.directory does not work at all (git 2.39.5, 2.51.0)

6 messages from 2025-09-13 to 2025-09-17. Participants: Marc-Jano Knopp, Carlo Marcelo Arenas Belón, Jeff King.
Thread: https://gitlist.dev/t/64145

## Marc-Jano Knopp, 2025-09-13 17:38

Subject: safe.directory does not work at all (git 2.39.5, 2.51.0)
Message-ID: <duuus2ifgydpwpja6vccvxbcnxdgy6mc6h63okzv7xdqo23fpi@ermurkhms4j3>
URL: https://gitlist.dev/e/duuus2ifgydpwpja6vccvxbcnxdgy6mc6h63okzv7xdqo23fpi%40ermurkhms4j3

```
Hi everyone!


git seems to have freshly implemented some security measure, and the
documented solution / workaround / whatever does not seem to work at all.
For the first time in years, git does not work for me anymore, I cannot
push my changes to the remote repository.

See below for what was created using "git bugreport":


=====================================================================
Thank you for filling out a Git bug report!
Please answer the following questions to help us understand your issue.

- What did you do before the bug happened? (Steps to reproduce your issue)

  I created a shared bare repo on my.server, permissions for everything
  are 2770 (rwxrws---) for dirs and 660 (rw-rw----) for files in that
  remote repository, and all dirs and files belong to root:git. I have
  an account "myuser:git" on that server.
  
  Then I tried to clone it to my local PC, which failed to some new
  security measure git seems to have introduced recently:

--------- snip ---------
$ git clone myuser@my.server:/git/main/test.git
Cloning into 'test'...
fatal: detected dubious ownership in repository at '/git/main/test.git'
To add an exception for this directory, call:

        git config --global --add safe.directory /git/main/test.git
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
$
--------- snip ---------

  I did execute the suggested command, so that my ~/.gitconfig now
  (only) contains:

--------- snip ---------
[safe]
        directory = /git/main/test.git
--------- snip ---------
          
  but the error still occurs. Using "git -c safe.directory='....'"
  did not help, either.
  

- What did you expect to happen? (Expected behavior)

  I expected the disabling of the above security measure to work.
  Actually, I want safe.directory to be set to "*", but that does not
  work, either.
  

- What happened instead? (Actual behavior)

  See above.


- What's different between what you expected and what actually happened?

  See above.


- Anything else you want to add:

  Can we please make suddenly occurring security measures and other
  breaking changes opt-in?


Please review the rest of the bug report below.
You can delete any lines you don't wish to share.


[System Info]
git version:
git version 2.39.5 (same error with 2.51.0 on a different PC)
cpu: x86_64
no commit associated with this build
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
uname: Linux 6.12.38+deb12-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.38-1~bpo12+1 (2025-07-27) x86_64
compiler info: gnuc: 12.2
libc info: glibc: 2.36
$SHELL (typically, interactive shell): /usr/bin/zsh


[Enabled Hooks]
not run from a git repository - no hooks to show
=====================================================================


Any help is appreciated!

If all else fails, I would downgrade to the last git version without
that security feature, if someone knows the version number that
introduced this feature ...


MJK

```

## Carlo Marcelo Arenas Belón, 2025-09-13 22:13

Subject: Re: safe.directory does not work at all (git 2.39.5, 2.51.0)
Message-ID: <lamzerhpp6kbgzbhztgaqvdcymaqvec232sen67t6wx4rmobih@lqqivgroonmp>
URL: https://gitlist.dev/e/lamzerhpp6kbgzbhztgaqvdcymaqvec232sen67t6wx4rmobih%40lqqivgroonmp
In-Reply-To: <duuus2ifgydpwpja6vccvxbcnxdgy6mc6h63okzv7xdqo23fpi@ermurkhms4j3>

```
On Sat, Sep 13, 2025 at 07:38:53PM -0800, Marc-Jano Knopp wrote:
> $ git clone myuser@my.server:/git/main/test.git
> Cloning into 'test'...
> fatal: detected dubious ownership in repository at '/git/main/test.git'
> To add an exception for this directory, call:
> 
>         git config --global --add safe.directory /git/main/test.git
> fatal: Could not read from remote repository.

it is a little confusing, but the message comes from the git command
running in "my.server".

doest it work if you run the same command after first doing ssh with "myuser"
account into "my.server"?

Carlo

```

## Marc-Jano Knopp, 2025-09-14 18:26

Subject: [SOLVED] Re: safe.directory does not work at all (git 2.39.5, 2.51.0)
Message-ID: <hbghuae56gm6ypox6q34mt4q6awoeb3itxsnmgpouycn7qodch@4pjsh24jhmqs>
URL: https://gitlist.dev/e/hbghuae56gm6ypox6q34mt4q6awoeb3itxsnmgpouycn7qodch%404pjsh24jhmqs
In-Reply-To: <lamzerhpp6kbgzbhztgaqvdcymaqvec232sen67t6wx4rmobih@lqqivgroonmp>

```
On Sun, 2025-09-14, at 00:13:14 (+0200), Carlo Marcelo Arenas Belón wrote:
> On Sat, Sep 13, 2025 at 07:38:53PM -0800, Marc-Jano Knopp wrote:
> > $ git clone myuser@my.server:/git/main/test.git
> > Cloning into 'test'...
> > fatal: detected dubious ownership in repository at '/git/main/test.git'
> > To add an exception for this directory, call:
> > 
> >         git config --global --add safe.directory /git/main/test.git
> > fatal: Could not read from remote repository.
> 
> it is a little confusing, but the message comes from the git command
> running in "my.server".

D'oh! Is there a way for the layman to see if a message comes from the
client or the server?


> doest it work if you run the same command after first doing ssh with "myuser"
> account into "my.server"?

Yes, it does! Thanks a million! *smooch* <3  :)


MJK

```

## Jeff King, 2025-09-15 02:23

Subject: Re: [SOLVED] Re: safe.directory does not work at all (git 2.39.5, 2.51.0)
Message-ID: <20250915022301.GA593748@coredump.intra.peff.net>
URL: https://gitlist.dev/e/20250915022301.GA593748%40coredump.intra.peff.net
In-Reply-To: <hbghuae56gm6ypox6q34mt4q6awoeb3itxsnmgpouycn7qodch@4pjsh24jhmqs>

```
On Sun, Sep 14, 2025 at 08:26:53PM +0200, Marc-Jano Knopp wrote:

> > > Cloning into 'test'...
> > > fatal: detected dubious ownership in repository at '/git/main/test.git'
> > > To add an exception for this directory, call:
> > > 
> > >         git config --global --add safe.directory /git/main/test.git
> > > fatal: Could not read from remote repository.
> > 
> > it is a little confusing, but the message comes from the git command
> > running in "my.server".
> 
> D'oh! Is there a way for the layman to see if a message comes from the
> client or the server?

Usually we try to pass error messages from the server over the sideband
channel, where the client prefixes them with "remote:" before showing
them to the user. Or report them via ERR packets, in which case the
client says something like "remote error: foo". Like:

  [this is an ERR packet; we are asking for a nonsense object id]
  $ git fetch origin 0000000000000000000000000000000000000001
  fatal: remote error: upload-pack: not our ref 0000000000000000000000000000000000000001

  [this is stderr from a server sub-process routed over the err
   sideband; I corrupted the server-side repo by removing one of
   its packfiles]
  $ git fetch $url
  remote: error: Could not read 576053ed5ad378490974fabe97e4bd59633d2d1e
  remote: fatal: Failed to traverse parents of commit a3287c454eb8f7b89d969e675768a6cfa258ad34
  remote: aborting due to possible repository corruption on the remote side.
  fatal: early EOF
  fatal: index-pack failed

But for the error you're seeing, it is happening within upload-pack
itself (the server-side process handling the request), it happens before
we have even established that the client can handle sideband data, and
it is a die() call from within library code that does not know about ERR
packets. So the message goes to upload-pack's stderr on the server side,
and then ssh just passes it back. In fact, you are a little lucky to see
it at all; for a clone over http, it would just go to the webserver's
log (or maybe /dev/null).

I do agree it is not very friendly, so I'm laying this out to help
brainstorm ideas to make it better. Some possible directions I can think
of:

  - could upload-pack install a die() handler that prints the message in
    an ERR packet? I worry a little that older versions of Git would not
    handle this great, as I don't think they were always prepared to see
    an ERR packet at any point. OTOH, it is probably better than sending
    nothing, which is what we do now.

  - could the client-side process (git-clone or git-fetch) intercept
    stderr from processes it spawns (ssh in this case, but also
    git-upload-pack directly for local-system clones) and prefix it with
    "remote:" or similar? That might help ssh and local system cases,
    but other transports like http wouldn't benefit at all. Also, it
    would probably involve forking off another process to consume
    stderr.

I dunno. I don't love either of those that much. And while it could help
things in general, I think the main clue in this case is just that the
error message refers to '/git/main/test.git'. And that path is only
meaningful on the server, since the url was my.server:/git/main/test.git.
Knowing that the config advice is _also_ coming from the server is
probably the key subtle bit, though.

-Peff

```

## Jeff King, 2025-09-15 02:46

Subject: Re: [SOLVED] Re: safe.directory does not work at all (git 2.39.5, 2.51.0)
Message-ID: <20250915024630.GA595592@coredump.intra.peff.net>
URL: https://gitlist.dev/e/20250915024630.GA595592%40coredump.intra.peff.net
In-Reply-To: <20250915022301.GA593748@coredump.intra.peff.net>

```
On Sun, Sep 14, 2025 at 10:23:01PM -0400, Jeff King wrote:

>   - could upload-pack install a die() handler that prints the message in
>     an ERR packet? I worry a little that older versions of Git would not
>     handle this great, as I don't think they were always prepared to see
>     an ERR packet at any point. OTOH, it is probably better than sending
>     nothing, which is what we do now.

Just for fun, I tried the patch below on v2.39.5:

diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index f446ff04f6..ad40143beb 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -13,6 +13,21 @@ static const char * const upload_pack_usage[] = {
 	NULL
 };
 
+NORETURN
+static void send_err_pkt_on_die(const char *fmt, va_list ap)
+{
+	struct strbuf buf = STRBUF_INIT;
+
+	/* format into a buf since interfaces below do not handle va_list */
+	strbuf_vaddf(&buf, fmt, ap);
+
+	/* write our ERR packet */
+	packet_write_fmt_gently(1, "ERR %s", buf.buf);
+
+	/* and then do the usual die to stderr */
+	exit(die_message("%s", buf.buf));
+}
+
 int cmd_upload_pack(int argc, const char **argv, const char *prefix)
 {
 	const char *dir;
@@ -38,6 +53,8 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
 	/* TODO: This should use NO_LAZY_FETCH_ENVIRONMENT */
 	xsetenv("GIT_NO_LAZY_FETCH", "1", 0);
 
+	set_die_routine(send_err_pkt_on_die);
+
 	argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
 
 	if (argc != 1)


The results are...not great. You get every message twice, of course,
because we still print it to stderr. Though that could easily be fixed.
But for the multi-line message in question, the "remote error" part is
hard to see amidst the other lines:

  Cloning into 'foo'...
  fatal: detected dubious ownership in repository at '/tmp/foo.git'
  To add an exception for this directory, call:
  
  	git config --global --add safe.directory /tmp/foo.git
  fatal: remote error: detected dubious ownership in repository at '/tmp/foo.git'
  To add an exception for this directory, call:
  
  	git config --global --add safe.directory /tmp/foo.git

Probably it would help to look for newlines and prefix every line with
"remote: or similar. But an even bigger problem is that we die
immediately on seeing the remote ERR packet, so we miss out on any local
error messages. An obvious one is trying to clone something that doesn't
exist at all. We used to say:

  Cloning into 'does-not-exist'...
  fatal: '/tmp/does-not-exist.git' does not appear to be a git repository
  fatal: Could not read from remote repository.

  Please make sure you have the correct access rights
  and the repository exists.

Noting that we saw an error from the remote side and giving some hints.
And the stderr from the other side is enough to give us the more
specific message (though again, over http the user would not get that
stderr message; however, if we get a 404 we do show a useful message).

But with the patch above we just relay what the other side says:

  Cloning into 'does-not-exist'...
  fatal: '/tmp/does-not-exist.git' does not appear to be a git repository
  fatal: remote error: '/tmp/does-not-exist.git' does not appear to be a git repository

which seems worse to me.

So probably not a very productive direction. Oh well.

-Peff

```

## Marc-Jano Knopp, 2025-09-17 20:22

Subject: Re: [SOLVED] Re: safe.directory does not work at all (git 2.39.5, 2.51.0)
Message-ID: <vuldcnyfwo6nii35kpwzpjws74hvgbeesovjpq4r5n2244usry@svmiikrnpviu>
URL: https://gitlist.dev/e/vuldcnyfwo6nii35kpwzpjws74hvgbeesovjpq4r5n2244usry%40svmiikrnpviu
In-Reply-To: <20250915022301.GA593748@coredump.intra.peff.net>

```
On Mon, 2025-09-15, at 04:23:01 (+0200), Jeff King wrote:
[...]
> I dunno. I don't love either of those that much. And while it could help
> things in general, I think the main clue in this case is just that the
> error message refers to '/git/main/test.git'. And that path is only
> meaningful on the server, since the url was my.server:/git/main/test.git.
Good point!

> Knowing that the config advice is _also_ coming from the server is
> probably the key subtle bit, though.
Yeah, the keyword "remote" would probably have been successfully caught
by my brain's pattern matching algorithm ...


MJK

```
