Compare commits

...

10 Commits

Author SHA1 Message Date
openeuler-ci-bot
81568744cf
!155 [sync] PR-154: Fix CVE-2024-32021 CVE-2024-32004 CVE-2024-32020 CVE-2024-32465
From: @openeuler-sync-bot 
Reviewed-by: @openeuler-basic 
Signed-off-by: @openeuler-basic
2024-05-17 09:57:02 +00:00
fly_fzc
7677bb732d Fix CVE-2024-32021 CVE-2024-32004 CVE-2024-32020 CVE-2024-32465
(cherry picked from commit ac09e19f0083e1f2eaf63730195a03afff734ffe)
2024-05-17 11:25:26 +08:00
openeuler-ci-bot
a16416f983
!149 [sync] PR-140: Fix CVE-2024-32002
From: @openeuler-sync-bot 
Reviewed-by: @huyubiao, @dillon_chen 
Signed-off-by: @dillon_chen
2024-05-17 01:58:07 +00:00
qiaojijun
9331f35363 Fix CVE-2024-32002
Signed-off-by: qiaojijun <qiaojijun@kylinos.cn>
(cherry picked from commit ea9184ed9062047ae8028b5715cc5bb5f2d59d2e)
2024-05-16 22:50:55 +08:00
openeuler-ci-bot
1dddfb3d0d
!136 Fix t9001-send-email.sh test error
From: @fly_fzc 
Reviewed-by: @openeuler-basic 
Signed-off-by: @openeuler-basic
2024-04-08 11:12:20 +00:00
fly_fzc
dc82881339 Fix t9001-send-email.sh test error 2024-04-08 17:10:44 +08:00
openeuler-ci-bot
a7a2f6c238
!134 update version to 2.43.0
From: @fly_fzc 
Reviewed-by: @dillon_chen 
Signed-off-by: @dillon_chen
2024-01-02 08:23:37 +00:00
fly_fzc
32ec62bfa8 update version to 2.43.0 2023-12-15 17:55:33 +08:00
openeuler-ci-bot
0b06cbbedf
!133 update version to 2.41.0
From: @fly_fzc 
Reviewed-by: @openeuler-basic 
Signed-off-by: @openeuler-basic
2023-07-18 06:29:29 +00:00
fly_fzc
0750433b8b update version to 2.41.0 2023-07-14 14:13:09 +08:00
26 changed files with 1018 additions and 1809 deletions

View File

@ -1,101 +0,0 @@
From 15ea1198bae31e248e01261b0163d024c0351695 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Fri, 16 Dec 2022 20:41:28 +0100
Subject: [PATCH] Move is_<platform> functions to the beginning
We need these in `_which` and they should be defined before that
function's definition.
This commit is best viewed with `--color-moved`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
git-gui/git-gui.sh | 58 +++++++++++++++++++++++++---------------------
1 file changed, 31 insertions(+), 27 deletions(-)
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 0fe60f80cc..f779fc9268 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -44,6 +44,37 @@ if {[catch {package require Tcl 8.5} err]
catch {rename send {}} ; # What an evil concept...
+######################################################################
+##
+## Enabling platform-specific code paths
+
+proc is_MacOSX {} {
+ if {[tk windowingsystem] eq {aqua}} {
+ return 1
+ }
+ return 0
+}
+
+proc is_Windows {} {
+ if {$::tcl_platform(platform) eq {windows}} {
+ return 1
+ }
+ return 0
+}
+
+set _iscygwin {}
+proc is_Cygwin {} {
+ global _iscygwin
+ if {$_iscygwin eq {}} {
+ if {[string match "CYGWIN_*" $::tcl_platform(os)]} {
+ set _iscygwin 1
+ } else {
+ set _iscygwin 0
+ }
+ }
+ return $_iscygwin
+}
+
######################################################################
##
## locate our library
@@ -163,7 +194,6 @@ set _isbare {}
set _gitexec {}
set _githtmldir {}
set _reponame {}
-set _iscygwin {}
set _search_path {}
set _shellpath {@@SHELL_PATH@@}
@@ -252,32 +282,6 @@ proc reponame {} {
return $::_reponame
}
-proc is_MacOSX {} {
- if {[tk windowingsystem] eq {aqua}} {
- return 1
- }
- return 0
-}
-
-proc is_Windows {} {
- if {$::tcl_platform(platform) eq {windows}} {
- return 1
- }
- return 0
-}
-
-proc is_Cygwin {} {
- global _iscygwin
- if {$_iscygwin eq {}} {
- if {[string match "CYGWIN_*" $::tcl_platform(os)]} {
- set _iscygwin 1
- } else {
- set _iscygwin 0
- }
- }
- return $_iscygwin
-}
-
proc is_enabled {option} {
global enabled_options
if {[catch {set on $enabled_options($option)}]} {return 0}
--
2.27.0

View File

@ -1,135 +0,0 @@
From 24f3f5833430d814f2c62220494741ea3d8cf4b3 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Mon, 5 Dec 2022 14:37:41 +0100
Subject: [PATCH] Move the `_which` function (almost) to the top
We are about to make use of the `_which` function to address
CVE-2022-41953 by overriding Tcl/Tk's unsafe PATH lookup on Windows.
In preparation for that, let's move it close to the top of the file to
make sure that even early `exec` calls that happen during the start-up
of Git GUI benefit from the fix.
This commit is best viewed with `--color-moved`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
git-gui/git-gui.sh | 88 ++++++++++++++++++++++++----------------------
1 file changed, 46 insertions(+), 42 deletions(-)
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index f779fc9268..b0eb5a6ae4 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -75,6 +75,52 @@ proc is_Cygwin {} {
return $_iscygwin
}
+######################################################################
+##
+## PATH lookup
+
+set _search_path {}
+proc _which {what args} {
+ global env _search_exe _search_path
+
+ if {$_search_path eq {}} {
+ if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
+ set _search_path [split [exec cygpath \
+ --windows \
+ --path \
+ --absolute \
+ $env(PATH)] {;}]
+ set _search_exe .exe
+ } elseif {[is_Windows]} {
+ set gitguidir [file dirname [info script]]
+ regsub -all ";" $gitguidir "\\;" gitguidir
+ set env(PATH) "$gitguidir;$env(PATH)"
+ set _search_path [split $env(PATH) {;}]
+ # Skip empty `PATH` elements
+ set _search_path [lsearch -all -inline -not -exact \
+ $_search_path ""]
+ set _search_exe .exe
+ } else {
+ set _search_path [split $env(PATH) :]
+ set _search_exe {}
+ }
+ }
+
+ if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
+ set suffix {}
+ } else {
+ set suffix $_search_exe
+ }
+
+ foreach p $_search_path {
+ set p [file join $p $what$suffix]
+ if {[file exists $p]} {
+ return [file normalize $p]
+ }
+ }
+ return {}
+}
+
######################################################################
##
## locate our library
@@ -194,7 +240,6 @@ set _isbare {}
set _gitexec {}
set _githtmldir {}
set _reponame {}
-set _search_path {}
set _shellpath {@@SHELL_PATH@@}
set _trace [lsearch -exact $argv --trace]
@@ -444,47 +489,6 @@ proc _git_cmd {name} {
return $v
}
-proc _which {what args} {
- global env _search_exe _search_path
-
- if {$_search_path eq {}} {
- if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
- set _search_path [split [exec cygpath \
- --windows \
- --path \
- --absolute \
- $env(PATH)] {;}]
- set _search_exe .exe
- } elseif {[is_Windows]} {
- set gitguidir [file dirname [info script]]
- regsub -all ";" $gitguidir "\\;" gitguidir
- set env(PATH) "$gitguidir;$env(PATH)"
- set _search_path [split $env(PATH) {;}]
- # Skip empty `PATH` elements
- set _search_path [lsearch -all -inline -not -exact \
- $_search_path ""]
- set _search_exe .exe
- } else {
- set _search_path [split $env(PATH) :]
- set _search_exe {}
- }
- }
-
- if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
- set suffix {}
- } else {
- set suffix $_search_exe
- }
-
- foreach p $_search_path {
- set p [file join $p $what$suffix]
- if {[file exists $p]} {
- return [file normalize $p]
- }
- }
- return {}
-}
-
# Test a file for a hashbang to identify executable scripts on Windows.
proc is_shellscript {filename} {
if {![file exists $filename]} {return 0}
--
2.27.0

View File

@ -1,125 +0,0 @@
From 2dd84542702a038496a23af4da8ad8059d0da91f Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Wed, 23 Nov 2022 09:31:06 +0100
Subject: [PATCH] Work around Tcl's default `PATH` lookup
As per https://www.tcl.tk/man/tcl8.6/TclCmd/exec.html#M23, Tcl's `exec`
function goes out of its way to imitate the highly dangerous path lookup
of `cmd.exe`, but _of course_ only on Windows:
If a directory name was not specified as part of the application
name, the following directories are automatically searched in
order when attempting to locate the application:
The directory from which the Tcl executable was loaded.
The current directory.
The Windows 32-bit system directory.
The Windows home directory.
The directories listed in the path.
The dangerous part is the second item, of course: `exec` _prefers_
executables in the current directory to those that are actually in the
`PATH`.
It is almost as if people wanted to Windows users vulnerable,
specifically.
To avoid that, Git GUI already has the `_which` function that does not
imitate that dangerous practice when looking up executables in the
search path.
However, Git GUI currently fails to use that function e.g. when trying to
execute `aspell` for spell checking.
That is not only dangerous but combined with Tcl's unfortunate default
behavior and with the fact that Git GUI tries to spell-check a
repository just after cloning, leads to a critical Remote Code Execution
vulnerability.
Let's override both `exec` and `open` to always use `_which` instead of
letting Tcl perform the path lookup, to prevent this attack vector.
This addresses CVE-2022-41953.
For more details, see
https://github.com/git-for-windows/git/security/advisories/GHSA-v4px-mx59-w99c
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
git-gui/git-gui.sh | 56 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index b0eb5a6ae4..cb92bba1c4 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -121,6 +121,62 @@ proc _which {what args} {
return {}
}
+proc sanitize_command_line {command_line from_index} {
+ set i $from_index
+ while {$i < [llength $command_line]} {
+ set cmd [lindex $command_line $i]
+ if {[file pathtype $cmd] ne "absolute"} {
+ set fullpath [_which $cmd]
+ if {$fullpath eq ""} {
+ throw {NOT-FOUND} "$cmd not found in PATH"
+ }
+ lset command_line $i $fullpath
+ }
+
+ # handle piped commands, e.g. `exec A | B`
+ for {incr i} {$i < [llength $command_line]} {incr i} {
+ if {[lindex $command_line $i] eq "|"} {
+ incr i
+ break
+ }
+ }
+ }
+ return $command_line
+}
+
+# Override `exec` to avoid unsafe PATH lookup
+
+rename exec real_exec
+
+proc exec {args} {
+ # skip options
+ for {set i 0} {$i < [llength $args]} {incr i} {
+ set arg [lindex $args $i]
+ if {$arg eq "--"} {
+ incr i
+ break
+ }
+ if {[string range $arg 0 0] ne "-"} {
+ break
+ }
+ }
+ set args [sanitize_command_line $args $i]
+ uplevel 1 real_exec $args
+}
+
+# Override `open` to avoid unsafe PATH lookup
+
+rename open real_open
+
+proc open {args} {
+ set arg0 [lindex $args 0]
+ if {[string range $arg0 0 0] eq "|"} {
+ set command_line [string trim [string range $arg0 1 end]]
+ lset args 0 "| [sanitize_command_line $command_line 0]"
+ }
+ uplevel 1 real_open $args
+}
+
######################################################################
##
## locate our library
--
2.27.0

View File

@ -1,61 +0,0 @@
From 9121f5c92c781f6e6415b17014c8c6ac864d2e70 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Sun, 4 Dec 2022 22:56:08 +0100
Subject: [PATCH] is_Cygwin: avoid `exec`ing anything
The `is_Cygwin` function is used, among other things, to determine
how executables are discovered in the `PATH` list by the `_which` function.
We are about to change the behavior of the `_which` function on Windows
(but not Cygwin): On Windows, we want it to ignore empty elements of the
`PATH` instead of treating them as referring to the current directory
(which is a "legacy feature" according to
https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html#tag_08_03,
but apparently not explicitly deprecated, the POSIX documentation is
quite unclear on that even if the Cygwin project itself considers it to
be deprecated: https://github.com/cygwin/cygwin/commit/fc74dbf22f5c).
This is important because on Windows, `exec` does something very unsafe
by default (unless we're running a Cygwin version of Tcl, which follows
Unix semantics).
However, we try to `exec` something _inside_ `is_Cygwin` to determine
whether we're running within Cygwin or not, i.e. before we determined
whether we need to handle `PATH` specially or not. That's a Catch-22.
Therefore, and because it is much cleaner anyway, use the
`$::tcl_platform(os)` value which is guaranteed to start with `CYGWIN_`
when running a Cygwin variant of Tcl/Tk, instead of executing `cygpath
--windir`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
git-gui/git-gui.sh | 12 ++----------
1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 0cf625ca01..0fe60f80cc 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -269,16 +269,8 @@ proc is_Windows {} {
proc is_Cygwin {} {
global _iscygwin
if {$_iscygwin eq {}} {
- if {$::tcl_platform(platform) eq {windows}} {
- if {[catch {set p [exec cygpath --windir]} err]} {
- set _iscygwin 0
- } else {
- set _iscygwin 1
- # Handle MSys2 which is only cygwin when MSYSTEM is MSYS.
- if {[info exists ::env(MSYSTEM)] && $::env(MSYSTEM) ne "MSYS"} {
- set _iscygwin 0
- }
- }
+ if {[string match "CYGWIN_*" $::tcl_platform(os)]} {
+ set _iscygwin 1
} else {
set _iscygwin 0
}
--
2.27.0

View File

@ -1,170 +0,0 @@
From 5ef19b63bf709cf39059bf67d97ab1dd22ef4a59 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Wed, 23 Nov 2022 09:12:49 +0100
Subject: [PATCH] windows: ignore empty `PATH` elements
When looking up an executable via the `_which` function, Git GUI
imitates the `execlp()` strategy where the environment variable `PATH`
is interpreted as a list of paths in which to search.
For historical reasons, stemming from the olden times when it was
uncommon to download a lot of files from the internet into the current
directory, empty elements in this list are treated as if the current
directory had been specified.
Nowadays, of course, this treatment is highly dangerous as the current
directory often contains files that have just been downloaded and not
yet been inspected by the user. Unix/Linux users are essentially
expected to be very, very careful to simply not add empty `PATH`
elements, i.e. not to make use of that feature.
On Windows, however, it is quite common for `PATH` to contain empty
elements by mistake, e.g. as an unintended left-over entry when an
application was installed from the Windows Store and then uninstalled
manually.
While it would probably make most sense to safe-guard not only Windows
users, it seems to be common practice to ignore these empty `PATH`
elements _only_ on Windows, but not on other platforms.
Sadly, this practice is followed inconsistently between different
software projects, where projects with few, if any, Windows-based
contributors tend to be less consistent or even "blissful" about it.
Here is a non-exhaustive list:
Cygwin:
It specifically "eats" empty paths when converting path lists to
POSIX: https://github.com/cygwin/cygwin/commit/753702223c7d
I.e. it follows the common practice.
PowerShell:
It specifically ignores empty paths when searching the `PATH`.
The reason for this is apparently so self-evident that it is not
even mentioned here:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_environment_variables#path-information
I.e. it follows the common practice.
CMD:
Oh my, CMD. Let's just forget about it, nobody in their right
(security) mind takes CMD as inspiration. It is so unsafe by
default that we even planned on dropping `Git CMD` from Git for
Windows altogether, and only walked back on that plan when we
found a super ugly hack, just to keep Git's users secure by
default:
https://github.com/git-for-windows/MINGW-packages/commit/82172388bb51
So CMD chooses to hide behind the battle cry "Works as
Designed!" that all too often leaves users vulnerable. CMD is
probably the most prominent project whose lead you want to avoid
following in matters of security.
Win32 API (`CreateProcess()`)
Just like CMD, `CreateProcess()` adheres to the original design
of the path lookup in the name of backward compatibility (see
https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw
for details):
If the file name does not contain a directory path, the
system searches for the executable file in the following
sequence:
1. The directory from which the application loaded.
2. The current directory for the parent process.
[...]
I.e. the Win32 API itself chooses backwards compatibility over
users' safety.
Git LFS:
There have been not one, not two, but three security advisories
about Git LFS executing executables from the current directory by
mistake. As part of one of them, a change was introduced to stop
treating empty `PATH` elements as equivalent to `.`:
https://github.com/git-lfs/git-lfs/commit/7cd7bb0a1f0d
I.e. it follows the common practice.
Go:
Go does not follow the common practice, and you can think about
that what you want:
https://github.com/golang/go/blob/go1.19.3/src/os/exec/lp_windows.go#L114-L135
https://github.com/golang/go/blob/go1.19.3/src/path/filepath/path_windows.go#L108-L137
Git Credential Manager:
It tries to imitate Git LFS, but unfortunately misses the empty
`PATH` element handling. As of time of writing, this is in the
process of being fixed:
https://github.com/GitCredentialManager/git-credential-manager/pull/968
So now that we have established that it is a common practice to ignore
empty `PATH` elements on Windows, let's assess this commit's change
using Schneier's Five-Step Process
(https://www.schneier.com/crypto-gram/archives/2002/0415.html#1):
Step 1: What problem does it solve?
It prevents an entire class of Remote Code Execution exploits via
Git GUI's `Clone` functionality.
Step 2: How well does it solve that problem?
Very well. It prevents the attack vector of luring an unsuspecting
victim into cloning an executable into the worktree root directory
that Git GUI immediately executes.
Step 3: What other security problems does it cause?
Maybe non-security problems: If a project (ab-)uses the unsafe
`PATH` lookup. That would not only be unsafe, though, but
fragile in the first place because it would break when running
in a subdirectory. Therefore I would consider this a scenario
not worth keeping working.
Step 4: What are the costs of this measure?
Almost nil, except for the time writing up this commit message
;-)
Step 5: Given the answers to steps two through four, is the security
measure worth the costs?
Yes. Keeping Git's users Secure By Default is worth it. It's a
tiny price to pay compared to the damages even a single
successful exploit can cost.
So let's follow that common practice in Git GUI, too.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
git-gui/git-gui.sh | 3 +++
1 file changed, 3 insertions(+)
diff --git a/git-gui/git-gui.sh b/git-gui/git-gui.sh
index 201524c34e..0cf625ca01 100755
--- a/git-gui/git-gui.sh
+++ b/git-gui/git-gui.sh
@@ -464,6 +464,9 @@ proc _which {what args} {
regsub -all ";" $gitguidir "\\;" gitguidir
set env(PATH) "$gitguidir;$env(PATH)"
set _search_path [split $env(PATH) {;}]
+ # Skip empty `PATH` elements
+ set _search_path [lsearch -all -inline -not -exact \
+ $_search_path ""]
set _search_exe .exe
} else {
set _search_path [split $env(PATH) :]
--
2.27.0

View File

@ -1,46 +0,0 @@
From 0227130244c007870c106fc613903d078730e45c Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Date: Thu, 12 Jan 2023 01:05:02 +0100
Subject: [PATCH] attr: adjust a mismatched data type
On platforms where `size_t` does not have the same width as `unsigned
long`, passing a pointer to the former when a pointer to the latter is
expected can lead to problems.
Windows and 32-bit Linux are among the affected platforms.
In this instance, we want to store the size of the blob that was read in
that variable. However, `read_blob_data_from_index()` passes that
pointer to `read_object_file()` which expects an `unsigned long *`.
Which means that on affected platforms, the variable is not fully
populated and part of its value is left uninitialized. (On Big-Endian
platforms, this problem would be even worse.)
The consequence is that depending on the uninitialized memory's
contents, we may erroneously reject perfectly fine attributes.
Let's address this by passing a pointer to a variable of the expected
data type.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
attr.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/attr.c b/attr.c
index f9316d14ba..c6498553db 100644
--- a/attr.c
+++ b/attr.c
@@ -745,7 +745,7 @@ static struct attr_stack *read_attr_from_index(const struct index_state *istate,
struct attr_stack *res;
char *buf, *sp;
int lineno = 0;
- size_t size;
+ unsigned long size;
if (!istate)
return NULL;
--
2.27.0

View File

@ -1,117 +0,0 @@
From cf8f6ce02a13f4d1979a53241afbee15a293fce9 Mon Sep 17 00:00:00 2001
From: Taylor Blau <me@ttaylorr.com>
Date: Tue, 24 Jan 2023 19:43:48 -0500
Subject: [PATCH] clone: delay picking a transport until after get_repo_path()
In the previous commit, t5619 demonstrates an issue where two calls to
`get_repo_path()` could trick Git into using its local clone mechanism
in conjunction with a non-local transport.
That sequence is:
- the starting state is that the local path https:/example.com/foo is a
symlink that points to ../../../.git/modules/foo. So it's dangling.
- get_repo_path() sees that no such path exists (because it's
dangling), and thus we do not canonicalize it into an absolute path
- because we're using --separate-git-dir, we create .git/modules/foo.
Now our symlink is no longer dangling!
- we pass the url to transport_get(), which sees it as an https URL.
- we call get_repo_path() again, on the url. This second call was
introduced by f38aa83f9a (use local cloning if insteadOf makes a
local URL, 2014-07-17). The idea is that we want to pull the url
fresh from the remote.c API, because it will apply any aliases.
And of course now it sees that there is a local file, which is a
mismatch with the transport we already selected.
The issue in the above sequence is calling `transport_get()` before
deciding whether or not the repository is indeed local, and not passing
in an absolute path if it is local.
This is reminiscent of a similar bug report in [1], where it was
suggested to perform the `insteadOf` lookup earlier. Taking that
approach may not be as straightforward, since the intent is to store the
original URL in the config, but to actually fetch from the insteadOf
one, so conflating the two early on is a non-starter.
Note: we pass the path returned by `get_repo_path(remote->url[0])`,
which should be the same as `repo_name` (aside from any `insteadOf`
rewrites).
We *could* pass `absolute_pathdup()` of the same argument, which
86521acaca (Bring local clone's origin URL in line with that of a remote
clone, 2008-09-01) indicates may differ depending on the presence of
".git/" for a non-bare repo. That matters for forming relative submodule
paths, but doesn't matter for the second call, since we're just feeding
it to the transport code, which is fine either way.
[1]: https://lore.kernel.org/git/CAMoD=Bi41mB3QRn3JdZL-FGHs4w3C2jGpnJB-CqSndO7FMtfzA@mail.gmail.com/
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
builtin/clone.c | 8 ++++----
t/t5619-clone-local-ambiguous-transport.sh | 15 +++++++++++----
2 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/builtin/clone.c b/builtin/clone.c
index e626073b1f..c042b2e256 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -1201,10 +1201,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix,
branch_top.buf);
- transport = transport_get(remote, remote->url[0]);
- transport_set_verbosity(transport, option_verbosity, option_progress);
- transport->family = family;
-
path = get_repo_path(remote->url[0], &is_bundle);
is_local = option_local != 0 && path && !is_bundle;
if (is_local) {
@@ -1224,6 +1220,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
}
if (option_local > 0 && !is_local)
warning(_("--local is ignored"));
+
+ transport = transport_get(remote, path ? path : remote->url[0]);
+ transport_set_verbosity(transport, option_verbosity, option_progress);
+ transport->family = family;
transport->cloning = 1;
if (is_bundle) {
diff --git a/t/t5619-clone-local-ambiguous-transport.sh b/t/t5619-clone-local-ambiguous-transport.sh
index 7ebd31a150..cce62bf78d 100755
--- a/t/t5619-clone-local-ambiguous-transport.sh
+++ b/t/t5619-clone-local-ambiguous-transport.sh
@@ -53,11 +53,18 @@ test_expect_success 'setup' '
git -C "$REPO" update-server-info
'
-test_expect_failure 'ambiguous transport does not lead to arbitrary file-inclusion' '
+test_expect_success 'ambiguous transport does not lead to arbitrary file-inclusion' '
git clone malicious clone &&
- git -C clone submodule update --init &&
-
- test_path_is_missing clone/.git/modules/sub/objects/secret
+ test_must_fail git -C clone submodule update --init 2>err &&
+
+ test_path_is_missing clone/.git/modules/sub/objects/secret &&
+ # We would actually expect "transport .file. not allowed" here,
+ # but due to quirks of the URL detection in Git, we mis-parse
+ # the absolute path as a bogus URL and die before that step.
+ #
+ # This works for now, and if we ever fix the URL detection, it
+ # is OK to change this to detect the transport error.
+ grep "protocol .* is not supported" err
'
test_done
--
2.27.0

View File

@ -1,150 +0,0 @@
From bffc762f87ae8d18c6001bf0044a76004245754c Mon Sep 17 00:00:00 2001
From: Taylor Blau <me@ttaylorr.com>
Date: Tue, 24 Jan 2023 19:43:51 -0500
Subject: [PATCH] dir-iterator: prevent top-level symlinks without
FOLLOW_SYMLINKS
When using the dir_iterator API, we first stat(2) the base path, and
then use that as a starting point to enumerate the directory's contents.
If the directory contains symbolic links, we will immediately die() upon
encountering them without the `FOLLOW_SYMLINKS` flag. The same is not
true when resolving the top-level directory, though.
As explained in a previous commit, this oversight in 6f054f9fb3
(builtin/clone.c: disallow `--local` clones with symlinks, 2022-07-28)
can be used as an attack vector to include arbitrary files on a victim's
filesystem from outside of the repository.
Prevent resolving top-level symlinks unless the FOLLOW_SYMLINKS flag is
given, which will cause clones of a repository with a symlink'd
"$GIT_DIR/objects" directory to fail.
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
dir-iterator.c | 13 +++++++++----
dir-iterator.h | 5 +++++
t/t0066-dir-iterator.sh | 27 ++++++++++++++++++++++++++-
t/t5604-clone-reference.sh | 16 ++++++++++++++++
4 files changed, 56 insertions(+), 5 deletions(-)
diff --git a/dir-iterator.c b/dir-iterator.c
index b17e9f970a..3764dd81a1 100644
--- a/dir-iterator.c
+++ b/dir-iterator.c
@@ -203,7 +203,7 @@ struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags)
{
struct dir_iterator_int *iter = xcalloc(1, sizeof(*iter));
struct dir_iterator *dir_iterator = &iter->base;
- int saved_errno;
+ int saved_errno, err;
strbuf_init(&iter->base.path, PATH_MAX);
strbuf_addstr(&iter->base.path, path);
@@ -213,10 +213,15 @@ struct dir_iterator *dir_iterator_begin(const char *path, unsigned int flags)
iter->flags = flags;
/*
- * Note: stat already checks for NULL or empty strings and
- * inexistent paths.
+ * Note: stat/lstat already checks for NULL or empty strings and
+ * nonexistent paths.
*/
- if (stat(iter->base.path.buf, &iter->base.st) < 0) {
+ if (iter->flags & DIR_ITERATOR_FOLLOW_SYMLINKS)
+ err = stat(iter->base.path.buf, &iter->base.st);
+ else
+ err = lstat(iter->base.path.buf, &iter->base.st);
+
+ if (err < 0) {
saved_errno = errno;
goto error_out;
}
diff --git a/dir-iterator.h b/dir-iterator.h
index 08229157c6..e3b6ff2800 100644
--- a/dir-iterator.h
+++ b/dir-iterator.h
@@ -61,6 +61,11 @@
* not the symlinks themselves, which is the default behavior. Broken
* symlinks are ignored.
*
+ * Note: setting DIR_ITERATOR_FOLLOW_SYMLINKS affects resolving the
+ * starting path as well (e.g., attempting to iterate starting at a
+ * symbolic link pointing to a directory without FOLLOW_SYMLINKS will
+ * result in an error).
+ *
* Warning: circular symlinks are also followed when
* DIR_ITERATOR_FOLLOW_SYMLINKS is set. The iteration may end up with
* an ELOOP if they happen and DIR_ITERATOR_PEDANTIC is set.
diff --git a/t/t0066-dir-iterator.sh b/t/t0066-dir-iterator.sh
index 92910e4e6c..c826f60f6d 100755
--- a/t/t0066-dir-iterator.sh
+++ b/t/t0066-dir-iterator.sh
@@ -109,7 +109,9 @@ test_expect_success SYMLINKS 'setup dirs with symlinks' '
mkdir -p dir5/a/c &&
ln -s ../c dir5/a/b/d &&
ln -s ../ dir5/a/b/e &&
- ln -s ../../ dir5/a/b/f
+ ln -s ../../ dir5/a/b/f &&
+
+ ln -s dir4 dir6
'
test_expect_success SYMLINKS 'dir-iterator should not follow symlinks by default' '
@@ -145,4 +147,27 @@ test_expect_success SYMLINKS 'dir-iterator should follow symlinks w/ follow flag
test_cmp expected-follow-sorted-output actual-follow-sorted-output
'
+test_expect_success SYMLINKS 'dir-iterator does not resolve top-level symlinks' '
+ test_must_fail test-tool dir-iterator ./dir6 >out &&
+
+ grep "ENOTDIR" out
+'
+
+test_expect_success SYMLINKS 'dir-iterator resolves top-level symlinks w/ follow flag' '
+ cat >expected-follow-sorted-output <<-EOF &&
+ [d] (a) [a] ./dir6/a
+ [d] (a/f) [f] ./dir6/a/f
+ [d] (a/f/c) [c] ./dir6/a/f/c
+ [d] (b) [b] ./dir6/b
+ [d] (b/c) [c] ./dir6/b/c
+ [f] (a/d) [d] ./dir6/a/d
+ [f] (a/e) [e] ./dir6/a/e
+ EOF
+
+ test-tool dir-iterator --follow-symlinks ./dir6 >out &&
+ sort out >actual-follow-sorted-output &&
+
+ test_cmp expected-follow-sorted-output actual-follow-sorted-output
+'
+
test_done
diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh
index 9d32f1c4a4..4ff21d7ccf 100755
--- a/t/t5604-clone-reference.sh
+++ b/t/t5604-clone-reference.sh
@@ -341,4 +341,20 @@ test_expect_success SYMLINKS 'clone repo with symlinked or unknown files at obje
test_must_be_empty T--shared.objects-symlinks.raw
'
+test_expect_success SYMLINKS 'clone repo with symlinked objects directory' '
+ test_when_finished "rm -fr sensitive malicious" &&
+
+ mkdir -p sensitive &&
+ echo "secret" >sensitive/file &&
+
+ git init malicious &&
+ rm -fr malicious/.git/objects &&
+ ln -s "$(pwd)/sensitive" ./malicious/.git/objects &&
+
+ test_must_fail git clone --local malicious clone 2>err &&
+
+ test_path_is_missing clone &&
+ grep "failed to start iterator over" err
+'
+
test_done
--
2.27.0

View File

@ -1,174 +0,0 @@
From 58325b93c5b6212697b088371809e9948fee8052 Mon Sep 17 00:00:00 2001
From: Taylor Blau <me@ttaylorr.com>
Date: Tue, 24 Jan 2023 19:43:45 -0500
Subject: [PATCH] t5619: demonstrate clone_local() with ambiguous transport
When cloning a repository, Git must determine (a) what transport
mechanism to use, and (b) whether or not the clone is local.
Since f38aa83f9a (use local cloning if insteadOf makes a local URL,
2014-07-17), the latter check happens after the remote has been
initialized, and references the remote's URL instead of the local path.
This is done to make it possible for a `url.<base>.insteadOf` rule to
convert a remote URL into a local one, in which case the `clone_local()`
mechanism should be used.
However, with a specially crafted repository, Git can be tricked into
using a non-local transport while still setting `is_local` to "1" and
using the `clone_local()` optimization. The below test case
demonstrates such an instance, and shows that it can be used to include
arbitrary (known) paths in the working copy of a cloned repository on a
victim's machine[^1], even if local file clones are forbidden by
`protocol.file.allow`.
This happens in a few parts:
1. We first call `get_repo_path()` to see if the remote is a local
path. If it is, we replace the repo name with its absolute path.
2. We then call `transport_get()` on the repo name and decide how to
access it. If it was turned into an absolute path in the previous
step, then we should always treat it like a file.
3. We use `get_repo_path()` again, and set `is_local` as appropriate.
But it's already too late to rewrite the repo name as an absolute
path, since we've already fed it to the transport code.
The attack works by including a submodule whose URL corresponds to a
path on disk. In the below example, the repository "sub" is reachable
via the dumb HTTP protocol at (something like):
http://127.0.0.1:NNNN/dumb/sub.git
However, the path "http:/127.0.0.1:NNNN/dumb" (that is, a top-level
directory called "http:", then nested directories "127.0.0.1:NNNN", and
"dumb") exists within the repository, too.
To determine this, it first picks the appropriate transport, which is
dumb HTTP. It then uses the remote's URL in order to determine whether
the repository exists locally on disk. However, the malicious repository
also contains an embedded stub repository which is the target of a
symbolic link at the local path corresponding to the "sub" repository on
disk (i.e., there is a symbolic link at "http:/127.0.0.1/dumb/sub.git",
pointing to the stub repository via ".git/modules/sub/../../../repo").
This stub repository fools Git into thinking that a local repository
exists at that URL and thus can be cloned locally. The affected call is
in `get_repo_path()`, which in turn calls `get_repo_path_1()`, which
locates a valid repository at that target.
This then causes Git to set the `is_local` variable to "1", and in turn
instructs Git to clone the repository using its local clone optimization
via the `clone_local()` function.
The exploit comes into play because the stub repository's top-level
"$GIT_DIR/objects" directory is a symbolic link which can point to an
arbitrary path on the victim's machine. `clone_local()` resolves the
top-level "objects" directory through a `stat(2)` call, meaning that we
read through the symbolic link and copy or hardlink the directory
contents at the destination of the link.
In other words, we can get steps (1) and (3) to disagree by leveraging
the dangling symlink to pick a non-local transport in the first step,
and then set is_local to "1" in the third step when cloning with
`--separate-git-dir`, which makes the symlink non-dangling.
This can result in data-exfiltration on the victim's machine when
sensitive data is at a known path (e.g., "/home/$USER/.ssh").
The appropriate fix is two-fold:
- Resolve the transport later on (to avoid using the local
clone optimization with a non-local transport).
- Avoid reading through the top-level "objects" directory when
(correctly) using the clone_local() optimization.
This patch merely demonstrates the issue. The following two patches will
implement each part of the above fix, respectively.
[^1]: Provided that any target directory does not contain symbolic
links, in which case the changes from 6f054f9fb3 (builtin/clone.c:
disallow `--local` clones with symlinks, 2022-07-28) will abort the
clone.
Reported-by: yvvdwf <yvvdwf@gmail.com>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
t/t5619-clone-local-ambiguous-transport.sh | 63 ++++++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100755 t/t5619-clone-local-ambiguous-transport.sh
diff --git a/t/t5619-clone-local-ambiguous-transport.sh b/t/t5619-clone-local-ambiguous-transport.sh
new file mode 100755
index 0000000000..7ebd31a150
--- /dev/null
+++ b/t/t5619-clone-local-ambiguous-transport.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+test_description='test local clone with ambiguous transport'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-httpd.sh"
+
+if ! test_have_prereq SYMLINKS
+then
+ skip_all='skipping test, symlink support unavailable'
+ test_done
+fi
+
+start_httpd
+
+REPO="$HTTPD_DOCUMENT_ROOT_PATH/sub.git"
+URI="$HTTPD_URL/dumb/sub.git"
+
+test_expect_success 'setup' '
+ mkdir -p sensitive &&
+ echo "secret" >sensitive/secret &&
+
+ git init --bare "$REPO" &&
+ test_commit_bulk -C "$REPO" --ref=main 1 &&
+
+ git -C "$REPO" update-ref HEAD main &&
+ git -C "$REPO" update-server-info &&
+
+ git init malicious &&
+ (
+ cd malicious &&
+
+ git submodule add "$URI" &&
+
+ mkdir -p repo/refs &&
+ touch repo/refs/.gitkeep &&
+ printf "ref: refs/heads/a" >repo/HEAD &&
+ ln -s "$(cd .. && pwd)/sensitive" repo/objects &&
+
+ mkdir -p "$HTTPD_URL/dumb" &&
+ ln -s "../../../.git/modules/sub/../../../repo/" "$URI" &&
+
+ git add . &&
+ git commit -m "initial commit"
+ ) &&
+
+ # Delete all of the references in our malicious submodule to
+ # avoid the client attempting to checkout any objects (which
+ # will be missing, and thus will cause the clone to fail before
+ # we can trigger the exploit).
+ git -C "$REPO" for-each-ref --format="delete %(refname)" >in &&
+ git -C "$REPO" update-ref --stdin <in &&
+ git -C "$REPO" update-server-info
+'
+
+test_expect_failure 'ambiguous transport does not lead to arbitrary file-inclusion' '
+ git clone malicious clone &&
+ git -C clone submodule update --init &&
+
+ test_path_is_missing clone/.git/modules/sub/objects/secret
+'
+
+test_done
--
2.27.0

View File

@ -1,179 +0,0 @@
From fade728df1221598f42d391cf377e9e84a32053f Mon Sep 17 00:00:00 2001
From: Patrick Steinhardt <ps@pks.im>
Date: Thu, 2 Feb 2023 11:54:34 +0100
Subject: [PATCH] apply: fix writing behind newly created symbolic links
When writing files git-apply(1) initially makes sure that none of the
files it is about to create are behind a symlink:
```
$ git init repo
Initialized empty Git repository in /tmp/repo/.git/
$ cd repo/
$ ln -s dir symlink
$ git apply - <<EOF
diff --git a/symlink/file b/symlink/file
new file mode 100644
index 0000000..e69de29
EOF
error: affected file 'symlink/file' is beyond a symbolic link
```
This safety mechanism is crucial to ensure that we don't write outside
of the repository's working directory. It can be fooled though when the
patch that is being applied creates the symbolic link in the first
place, which can lead to writing files in arbitrary locations.
Fix this by checking whether the path we're about to create is
beyond a symlink or not. Tightening these checks like this should be
fine as we already have these precautions in Git as explained
above. Ideally, we should update the check we do up-front before
starting to reflect the computed changes to the working tree so that
we catch this case as well, but as part of embargoed security work,
adding an equivalent check just before we try to write out a file
should serve us well as a reasonable first step.
Digging back into history shows that this vulnerability has existed
since at least Git v2.9.0. As Git v2.8.0 and older don't build on my
system anymore I cannot tell whether older versions are affected, as
well.
Reported-by: Joern Schneeweisz <jschneeweisz@gitlab.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
apply.c | 27 ++++++++++++++
t/t4115-apply-symlink.sh | 81 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 108 insertions(+)
diff --git a/apply.c b/apply.c
index 668b16e989..d80382c940 100644
--- a/apply.c
+++ b/apply.c
@@ -4400,6 +4400,33 @@ static int create_one_file(struct apply_state *state,
if (state->cached)
return 0;
+ /*
+ * We already try to detect whether files are beyond a symlink in our
+ * up-front checks. But in the case where symlinks are created by any
+ * of the intermediate hunks it can happen that our up-front checks
+ * didn't yet see the symlink, but at the point of arriving here there
+ * in fact is one. We thus repeat the check for symlinks here.
+ *
+ * Note that this does not make the up-front check obsolete as the
+ * failure mode is different:
+ *
+ * - The up-front checks cause us to abort before we have written
+ * anything into the working directory. So when we exit this way the
+ * working directory remains clean.
+ *
+ * - The checks here happen in the middle of the action where we have
+ * already started to apply the patch. The end result will be a dirty
+ * working directory.
+ *
+ * Ideally, we should update the up-front checks to catch what would
+ * happen when we apply the patch before we damage the working tree.
+ * We have all the information necessary to do so. But for now, as a
+ * part of embargoed security work, having this check would serve as a
+ * reasonable first step.
+ */
+ if (path_is_beyond_symlink(state, path))
+ return error(_("affected file '%s' is beyond a symbolic link"), path);
+
res = try_create_file(state, path, mode, buf, size);
if (res < 0)
return -1;
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index 872fcda6cb..1acb7b2582 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -44,4 +44,85 @@ test_expect_success 'apply --index symlink patch' '
'
+test_expect_success 'symlink setup' '
+ ln -s .git symlink &&
+ git add symlink &&
+ git commit -m "add symlink"
+'
+
+test_expect_success SYMLINKS 'symlink escape when creating new files' '
+ test_when_finished "git reset --hard && git clean -dfx" &&
+
+ cat >patch <<-EOF &&
+ diff --git a/symlink b/renamed-symlink
+ similarity index 100%
+ rename from symlink
+ rename to renamed-symlink
+ --
+ diff --git /dev/null b/renamed-symlink/create-me
+ new file mode 100644
+ index 0000000..039727e
+ --- /dev/null
+ +++ b/renamed-symlink/create-me
+ @@ -0,0 +1,1 @@
+ +busted
+ EOF
+
+ test_must_fail git apply patch 2>stderr &&
+ cat >expected_stderr <<-EOF &&
+ error: affected file ${SQ}renamed-symlink/create-me${SQ} is beyond a symbolic link
+ EOF
+ test_cmp expected_stderr stderr &&
+ ! test_path_exists .git/create-me
+'
+
+test_expect_success SYMLINKS 'symlink escape when modifying file' '
+ test_when_finished "git reset --hard && git clean -dfx" &&
+ touch .git/modify-me &&
+
+ cat >patch <<-EOF &&
+ diff --git a/symlink b/renamed-symlink
+ similarity index 100%
+ rename from symlink
+ rename to renamed-symlink
+ --
+ diff --git a/renamed-symlink/modify-me b/renamed-symlink/modify-me
+ index 1111111..2222222 100644
+ --- a/renamed-symlink/modify-me
+ +++ b/renamed-symlink/modify-me
+ @@ -0,0 +1,1 @@
+ +busted
+ EOF
+
+ test_must_fail git apply patch 2>stderr &&
+ cat >expected_stderr <<-EOF &&
+ error: renamed-symlink/modify-me: No such file or directory
+ EOF
+ test_cmp expected_stderr stderr &&
+ test_must_be_empty .git/modify-me
+'
+
+test_expect_success SYMLINKS 'symlink escape when deleting file' '
+ test_when_finished "git reset --hard && git clean -dfx && rm .git/delete-me" &&
+ touch .git/delete-me &&
+
+ cat >patch <<-EOF &&
+ diff --git a/symlink b/renamed-symlink
+ similarity index 100%
+ rename from symlink
+ rename to renamed-symlink
+ --
+ diff --git a/renamed-symlink/delete-me b/renamed-symlink/delete-me
+ deleted file mode 100644
+ index 1111111..0000000 100644
+ EOF
+
+ test_must_fail git apply patch 2>stderr &&
+ cat >expected_stderr <<-EOF &&
+ error: renamed-symlink/delete-me: No such file or directory
+ EOF
+ test_cmp expected_stderr stderr &&
+ test_path_is_file .git/delete-me
+'
+
test_done
--
2.27.0

View File

@ -1,90 +0,0 @@
From 9db05711c98efc14f414d4c87135a34c13586e0b Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Thu, 9 Mar 2023 16:02:54 +0100
Subject: [PATCH] apply --reject: overwrite existing `.rej` symlink if it
exists
The `git apply --reject` is expected to write out `.rej` files in case
one or more hunks fail to apply cleanly. Historically, the command
overwrites any existing `.rej` files. The idea being that
apply/reject/edit cycles are relatively common, and the generated `.rej`
files are not considered precious.
But the command does not overwrite existing `.rej` symbolic links, and
instead follows them. This is unsafe because the same patch could
potentially create such a symbolic link and point at arbitrary paths
outside the current worktree, and `git apply` would write the contents
of the `.rej` file into that location.
Therefore, let's make sure that any existing `.rej` file or symbolic
link is removed before writing it.
Reported-by: RyotaK <ryotak.mail@gmail.com>
Helped-by: Taylor Blau <me@ttaylorr.com>
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Linus Torvalds <torvalds@linuxfoundation.org>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
apply.c | 14 ++++++++++++--
t/t4115-apply-symlink.sh | 15 +++++++++++++++
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/apply.c b/apply.c
index d80382c940..6634e9c510 100644
--- a/apply.c
+++ b/apply.c
@@ -4558,7 +4558,7 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch)
FILE *rej;
char namebuf[PATH_MAX];
struct fragment *frag;
- int cnt = 0;
+ int fd, cnt = 0;
struct strbuf sb = STRBUF_INIT;
for (cnt = 0, frag = patch->fragments; frag; frag = frag->next) {
@@ -4598,7 +4598,17 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch)
memcpy(namebuf, patch->new_name, cnt);
memcpy(namebuf + cnt, ".rej", 5);
- rej = fopen(namebuf, "w");
+ fd = open(namebuf, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ if (fd < 0) {
+ if (errno != EEXIST)
+ return error_errno(_("cannot open %s"), namebuf);
+ if (unlink(namebuf))
+ return error_errno(_("cannot unlink '%s'"), namebuf);
+ fd = open(namebuf, O_CREAT | O_EXCL | O_WRONLY, 0666);
+ if (fd < 0)
+ return error_errno(_("cannot open %s"), namebuf);
+ }
+ rej = fdopen(fd, "w");
if (!rej)
return error_errno(_("cannot open %s"), namebuf);
diff --git a/t/t4115-apply-symlink.sh b/t/t4115-apply-symlink.sh
index 14e0f4d705..2d03c4e4d1 100755
--- a/t/t4115-apply-symlink.sh
+++ b/t/t4115-apply-symlink.sh
@@ -125,4 +125,19 @@ test_expect_success SYMLINKS 'symlink escape when deleting file' '
test_path_is_file .git/delete-me
'
+test_expect_success SYMLINKS '--reject removes .rej symlink if it exists' '
+ test_when_finished "git reset --hard && git clean -dfx" &&
+
+ test_commit file &&
+ echo modified >file.t &&
+ git diff -- file.t >patch &&
+ echo modified-again >file.t &&
+
+ ln -s foo file.t.rej &&
+ test_must_fail git apply patch --reject 2>err &&
+ test_i18ngrep "Rejected hunk" err &&
+ test_path_is_missing foo &&
+ test_path_is_file file.t.rej
+'
+
test_done
--
2.27.0

View File

@ -1,89 +0,0 @@
From c4137be0f5a6edf9a9044e6e43ecf4468c7a4046 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Wed, 22 Feb 2023 12:40:55 +0100
Subject: [PATCH] gettext: avoid using gettext if the locale dir is not present
In cc5e1bf99247 (gettext: avoid initialization if the locale dir is not
present, 2018-04-21) Git was taught to avoid a costly gettext start-up
when there are not even any localized messages to work with.
But we still called `gettext()` and `ngettext()` functions.
Which caused a problem in Git for Windows when the libgettext that is
consumed from the MSYS2 project stopped using a runtime prefix in
https://github.com/msys2/MINGW-packages/pull/10461
Due to that change, we now use an unintialized gettext machinery that
might get auto-initialized _using an unintended locale directory_:
`C:\mingw64\share\locale`.
Let's record the fact when the gettext initialization was skipped, and
skip calling the gettext functions accordingly.
This addresses CVE-2023-25815.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
gettext.c | 4 ++++
gettext.h | 7 ++++++-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/gettext.c b/gettext.c
index 1b564216d0..610d402fe7 100644
--- a/gettext.c
+++ b/gettext.c
@@ -109,6 +109,8 @@ static void init_gettext_charset(const char *domain)
setlocale(LC_CTYPE, "C");
}
+int git_gettext_enabled = 0;
+
void git_setup_gettext(void)
{
const char *podir = getenv(GIT_TEXT_DOMAIN_DIR_ENVIRONMENT);
@@ -130,6 +132,8 @@ void git_setup_gettext(void)
init_gettext_charset("git");
textdomain("git");
+ git_gettext_enabled = 1;
+
free(p);
}
diff --git a/gettext.h b/gettext.h
index bee52eb113..b96ab9d340 100644
--- a/gettext.h
+++ b/gettext.h
@@ -31,9 +31,11 @@
#define FORMAT_PRESERVING(n) __attribute__((format_arg(n)))
#ifndef NO_GETTEXT
+extern int git_gettext_enabled;
void git_setup_gettext(void);
int gettext_width(const char *s);
#else
+#define git_gettext_enabled (0)
static inline void git_setup_gettext(void)
{
}
@@ -48,6 +50,8 @@ static inline FORMAT_PRESERVING(1) const char *_(const char *msgid)
{
if (!*msgid)
return "";
+ if (!git_gettext_enabled)
+ return msgid;
return gettext(msgid);
}
@@ -56,6 +59,8 @@ static inline FORMAT_PRESERVING(1) const char *_(const char *msgid)
static inline FORMAT_PRESERVING(1) FORMAT_PRESERVING(2)
const char *Q_(const char *msgid, const char *plu, unsigned long n)
{
+ if (!git_gettext_enabled)
+ return n == 1 ? msgid : plu;
return ngettext(msgid, plu, n);
}
--
2.27.0

View File

@ -1,356 +0,0 @@
From 29198213c9163c1d552ee2bdbf78d2b09ccc98b8 Mon Sep 17 00:00:00 2001
From: Taylor Blau <me@ttaylorr.com>
Date: Thu, 6 Apr 2023 11:42:03 -0400
Subject: [PATCH 1/4] t1300: demonstrate failure when renaming sections with
long lines
When renaming a configuration section which has an entry whose length
exceeds the size of our buffer in config.c's implementation of
`git_config_copy_or_rename_section_in_file()`, Git will incorrectly
form a new configuration section with part of the data in the section
being removed.
In this instance, our first configuration file looks something like:
[b]
c = d <spaces> [a] e = f
[a]
g = h
Here, we have two configuration values, "b.c", and "a.g". The value "[a]
e = f" belongs to the configuration value "b.c", and does not form its
own section.
However, when renaming the section 'a' to 'xyz', Git will write back
"[xyz]\ne = f", but "[xyz]" is still attached to the value of "b.c",
which is why "e = f" on its own line becomes a new entry called "b.e".
A slightly different example embeds the section being renamed within
another section.
Demonstrate this failure in a test in t1300, which we will fix in the
following commit.
Co-authored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
---
t/t1300-config.sh | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 1a4156c70434f3..cd8f744160e8ba 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -613,6 +613,26 @@ test_expect_success 'renaming to bogus section is rejected' '
test_must_fail git config --rename-section branch.zwei "bogus name"
'
+test_expect_failure 'renaming a section with a long line' '
+ {
+ printf "[b]\\n" &&
+ printf " c = d %1024s [a] e = f\\n" " " &&
+ printf "[a] g = h\\n"
+ } >y &&
+ git config -f y --rename-section a xyz &&
+ test_must_fail git config -f y b.e
+'
+
+test_expect_failure 'renaming an embedded section with a long line' '
+ {
+ printf "[b]\\n" &&
+ printf " c = d %1024s [a] [foo] e = f\\n" " " &&
+ printf "[a] g = h\\n"
+ } >y &&
+ git config -f y --rename-section a xyz &&
+ test_must_fail git config -f y foo.e
+'
+
cat >> .git/config << EOF
[branch "zwei"] a = 1 [branch "vier"]
EOF
From a5bb10fd5e74101e7c07da93e7c32bbe60f6173a Mon Sep 17 00:00:00 2001
From: Taylor Blau <me@ttaylorr.com>
Date: Thu, 6 Apr 2023 14:07:58 -0400
Subject: [PATCH 2/4] config: avoid fixed-sized buffer when renaming/deleting a
section
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
When renaming (or deleting) a section of configuration, Git uses the
function `git_config_copy_or_rename_section_in_file()` to rewrite the
configuration file after applying the rename or deletion to the given
section.
To do this, Git repeatedly calls `fgets()` to read the existing
configuration data into a fixed size buffer.
When the configuration value under `old_name` exceeds the size of the
buffer, we will call `fgets()` an additional time even if there is no
newline in the configuration file, since our read length is capped at
`sizeof(buf)`.
If the first character of the buffer (after zero or more characters
satisfying `isspace()`) is a '[', Git will incorrectly treat it as
beginning a new section when the original section is being removed. In
other words, a configuration value satisfying this criteria can
incorrectly be considered as a new secftion instead of a variable in the
original section.
Avoid this issue by using a variable-width buffer in the form of a
strbuf rather than a fixed-with region on the stack. A couple of small
points worth noting:
- Using a strbuf will cause us to allocate arbitrary sizes to match
the length of each line. In practice, we don't expect any
reasonable configuration files to have lines that long, and a
bandaid will be introduced in a later patch to ensure that this is
the case.
- We are using strbuf_getwholeline() here instead of strbuf_getline()
in order to match `fgets()`'s behavior of leaving the trailing LF
character on the buffer (as well as a trailing NUL).
This could be changed later, but using strbuf_getwholeline() changes
the least about this function's implementation, so it is picked as
the safest path.
- It is temping to want to replace the loop to skip over characters
matching isspace() at the beginning of the buffer with a convenience
function like `strbuf_ltrim()`. But this is the wrong approach for a
couple of reasons:
First, it involves a potentially large and expensive `memmove()`
which we would like to avoid. Second, and more importantly, we also
*do* want to preserve those spaces to avoid changing the output of
other sections.
In all, this patch is a minimal replacement of the fixed-width buffer in
`git_config_copy_or_rename_section_in_file()` to instead use a `struct
strbuf`.
Reported-by: André Baptista <andre@ethiack.com>
Reported-by: Vítor Pinho <vitor@ethiack.com>
Helped-by: Patrick Steinhardt <ps@pks.im>
Co-authored-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
---
config.c | 13 +++++++------
t/t1300-config.sh | 4 ++--
2 files changed, 9 insertions(+), 8 deletions(-)
diff --git a/config.c b/config.c
index 1137bd73aff07c..524347676d0da0 100644
--- a/config.c
+++ b/config.c
@@ -3091,7 +3091,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
char *filename_buf = NULL;
struct lock_file lock = LOCK_INIT;
int out_fd;
- char buf[1024];
+ struct strbuf buf = STRBUF_INIT;
FILE *config_file = NULL;
struct stat st;
struct strbuf copystr = STRBUF_INIT;
@@ -3132,14 +3132,14 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
goto out;
}
- while (fgets(buf, sizeof(buf), config_file)) {
+ while (!strbuf_getwholeline(&buf, config_file, '\n')) {
unsigned i;
int length;
int is_section = 0;
- char *output = buf;
- for (i = 0; buf[i] && isspace(buf[i]); i++)
+ char *output = buf.buf;
+ for (i = 0; buf.buf[i] && isspace(buf.buf[i]); i++)
; /* do nothing */
- if (buf[i] == '[') {
+ if (buf.buf[i] == '[') {
/* it's a section */
int offset;
is_section = 1;
@@ -3158,7 +3158,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
strbuf_reset(&copystr);
}
- offset = section_name_match(&buf[i], old_name);
+ offset = section_name_match(&buf.buf[i], old_name);
if (offset > 0) {
ret++;
if (!new_name) {
@@ -3233,6 +3233,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
out_no_rollback:
free(filename_buf);
config_store_data_clear(&store);
+ strbuf_release(&buf);
return ret;
}
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index cd8f744160e8ba..24c13b91dbd669 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -613,7 +613,7 @@ test_expect_success 'renaming to bogus section is rejected' '
test_must_fail git config --rename-section branch.zwei "bogus name"
'
-test_expect_failure 'renaming a section with a long line' '
+test_expect_success 'renaming a section with a long line' '
{
printf "[b]\\n" &&
printf " c = d %1024s [a] e = f\\n" " " &&
@@ -623,7 +623,7 @@ test_expect_failure 'renaming a section with a long line' '
test_must_fail git config -f y b.e
'
-test_expect_failure 'renaming an embedded section with a long line' '
+test_expect_success 'renaming an embedded section with a long line' '
{
printf "[b]\\n" &&
printf " c = d %1024s [a] [foo] e = f\\n" " " &&
From e91cfe6085c4a61372d1f800b473b73b8d225d0d Mon Sep 17 00:00:00 2001
From: Taylor Blau <me@ttaylorr.com>
Date: Thu, 6 Apr 2023 14:28:53 -0400
Subject: [PATCH 3/4] config.c: avoid integer truncation in
`copy_or_rename_section_in_file()`
There are a couple of spots within `copy_or_rename_section_in_file()`
that incorrectly use an `int` to track an offset within a string, which
may truncate or wrap around to a negative value.
Historically it was impossible to have a line longer than 1024 bytes
anyway, since we used fgets() with a fixed-size buffer of exactly that
length. But the recent change to use a strbuf permits us to read lines
of arbitrary length, so it's possible for a malicious input to cause us
to overflow past INT_MAX and do an out-of-bounds array read.
Practically speaking, however, this should never happen, since it
requires 2GB section names or values, which are unrealistic in
non-malicious circumstances.
Co-authored-by: Jeff King <peff@peff.net>
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Taylor Blau <me@ttaylorr.com>
---
config.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/config.c b/config.c
index 524347676d0da0..e4189aa2d79f62 100644
--- a/config.c
+++ b/config.c
@@ -3027,9 +3027,10 @@ void git_config_set_multivar(const char *key, const char *value,
flags);
}
-static int section_name_match (const char *buf, const char *name)
+static size_t section_name_match (const char *buf, const char *name)
{
- int i = 0, j = 0, dot = 0;
+ size_t i = 0, j = 0;
+ int dot = 0;
if (buf[i] != '[')
return 0;
for (i = 1; buf[i] && buf[i] != ']'; i++) {
@@ -3133,15 +3134,14 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
}
while (!strbuf_getwholeline(&buf, config_file, '\n')) {
- unsigned i;
- int length;
+ size_t i, length;
int is_section = 0;
char *output = buf.buf;
for (i = 0; buf.buf[i] && isspace(buf.buf[i]); i++)
; /* do nothing */
if (buf.buf[i] == '[') {
/* it's a section */
- int offset;
+ size_t offset;
is_section = 1;
/*
From 3bb3d6bac5f2b496dfa2862dc1a84cbfa9b4449a Mon Sep 17 00:00:00 2001
From: Taylor Blau <me@ttaylorr.com>
Date: Wed, 12 Apr 2023 19:18:28 -0400
Subject: [PATCH 4/4] config.c: disallow overly-long lines in
`copy_or_rename_section_in_file()`
As a defense-in-depth measure to guard against any potentially-unknown
buffer overflows in `copy_or_rename_section_in_file()`, refuse to work
with overly-long lines in a gitconfig.
Signed-off-by: Taylor Blau <me@ttaylorr.com>
Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
config.c | 13 +++++++++++++
t/t1300-config.sh | 10 ++++++++++
2 files changed, 23 insertions(+)
diff --git a/config.c b/config.c
index e4189aa2d79f62..b8194dfd8a78af 100644
--- a/config.c
+++ b/config.c
@@ -3083,6 +3083,8 @@ static int section_name_is_ok(const char *name)
return 1;
}
+#define GIT_CONFIG_MAX_LINE_LEN (512 * 1024)
+
/* if new_name == NULL, the section is removed instead */
static int git_config_copy_or_rename_section_in_file(const char *config_filename,
const char *old_name,
@@ -3097,6 +3099,7 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
struct stat st;
struct strbuf copystr = STRBUF_INIT;
struct config_store_data store;
+ uint32_t line_nr = 0;
memset(&store, 0, sizeof(store));
@@ -3137,6 +3140,16 @@ static int git_config_copy_or_rename_section_in_file(const char *config_filename
size_t i, length;
int is_section = 0;
char *output = buf.buf;
+
+ line_nr++;
+
+ if (buf.len >= GIT_CONFIG_MAX_LINE_LEN) {
+ ret = error(_("refusing to work with overly long line "
+ "in '%s' on line %"PRIuMAX),
+ config_filename, (uintmax_t)line_nr);
+ goto out;
+ }
+
for (i = 0; buf.buf[i] && isspace(buf.buf[i]); i++)
; /* do nothing */
if (buf.buf[i] == '[') {
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index 24c13b91dbd669..de564cb8e587a6 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -633,6 +633,16 @@ test_expect_success 'renaming an embedded section with a long line' '
test_must_fail git config -f y foo.e
'
+test_expect_success 'renaming a section with an overly-long line' '
+ {
+ printf "[b]\\n" &&
+ printf " c = d %525000s e" " " &&
+ printf "[a] g = h\\n"
+ } >y &&
+ test_must_fail git config -f y --rename-section a xyz 2>err &&
+ test_i18ngrep "refusing to work with overly long line in .y. on line 2" err
+'
+
cat >> .git/config << EOF
[branch "zwei"] a = 1 [branch "vier"]
EOF

View File

@ -0,0 +1,164 @@
From 6393e6afd414ab9ebeffe069726440d397cae268 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Fri, 22 Mar 2024 11:19:22 +0100
Subject: [PATCH] backport CVE-2024-32002 submodules: submodule paths must not
contain symlinks
mainline inclusion
from v2.43.4
commit 97065761333fd62db1912d81b489db938d8c991d
category: bugfix
bugzilla: https://nvd.nist.gov/vuln/detail/CVE-2024-32002
CVE: CVE-2024-32002
When creating a submodule path, we must be careful not to follow
symbolic links. Otherwise we may follow a symbolic link pointing to
a gitdir (which are valid symbolic links!) e.g. while cloning.
On case-insensitive filesystems, however, we blindly replace a directory
that has been created as part of the `clone` operation with a symlink
when the path to the latter differs only in case from the former's path.
Let's simply avoid this situation by expecting not ever having to
overwrite any existing file/directory/symlink upon cloning. That way, we
won't even replace a directory that we just created.
This addresses CVE-2024-32002.
confliects:
t/t7406-submodule-update.sh
Reported-by: Filip Hejsek <filip.hejsek@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: qiaojijun <qiaojijun@kylinos.cn>
---
builtin/submodule--helper.c | 35 +++++++++++++++++++++++++++
t/t7406-submodule-update.sh | 48 +++++++++++++++++++++++++++++++++++++
2 files changed, 83 insertions(+)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index cce4645..c46d420 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1663,12 +1663,35 @@ static char *clone_submodule_sm_gitdir(const char *name)
return sm_gitdir;
}
+static int dir_contains_only_dotgit(const char *path)
+{
+ DIR *dir = opendir(path);
+ struct dirent *e;
+ int ret = 1;
+
+ if (!dir)
+ return 0;
+
+ e = readdir_skip_dot_and_dotdot(dir);
+ if (!e)
+ ret = 0;
+ else if (strcmp(DEFAULT_GIT_DIR_ENVIRONMENT, e->d_name) ||
+ (e = readdir_skip_dot_and_dotdot(dir))) {
+ error("unexpected item '%s' in '%s'", e->d_name, path);
+ ret = 0;
+ }
+
+ closedir(dir);
+ return ret;
+}
+
static int clone_submodule(const struct module_clone_data *clone_data,
struct string_list *reference)
{
char *p;
char *sm_gitdir = clone_submodule_sm_gitdir(clone_data->name);
char *sm_alternate = NULL, *error_strategy = NULL;
+ struct stat st;
struct child_process cp = CHILD_PROCESS_INIT;
const char *clone_data_path = clone_data->path;
char *to_free = NULL;
@@ -1682,6 +1705,10 @@ static int clone_submodule(const struct module_clone_data *clone_data,
"git dir"), sm_gitdir);
if (!file_exists(sm_gitdir)) {
+ if (clone_data->require_init && !stat(clone_data_path, &st) &&
+ !is_empty_dir(clone_data_path))
+ die(_("directory not empty: '%s'"), clone_data_path);
+
if (safe_create_leading_directories_const(sm_gitdir) < 0)
die(_("could not create directory '%s'"), sm_gitdir);
@@ -1726,6 +1753,14 @@ static int clone_submodule(const struct module_clone_data *clone_data,
if(run_command(&cp))
die(_("clone of '%s' into submodule path '%s' failed"),
clone_data->url, clone_data_path);
+
+ if (clone_data->require_init && !stat(clone_data_path, &st) &&
+ !dir_contains_only_dotgit(clone_data_path)) {
+ char *dot_git = xstrfmt("%s/.git", clone_data_path);
+ unlink(dot_git);
+ free(dot_git);
+ die(_("directory not empty: '%s'"), clone_data_path);
+ }
} else {
char *path;
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 8491b8c..1f98b01 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -1179,6 +1179,54 @@ test_expect_success 'submodule update --recursive skip submodules with strategy=
test_cmp expect.err actual.err
'
+test_expect_success CASE_INSENSITIVE_FS,SYMLINKS \
+ 'submodule paths must not follow symlinks' '
+
+ # This is only needed because we want to run this in a self-contained
+ # test without having to spin up an HTTP server; However, it would not
+ # be needed in a real-world scenario where the submodule is simply
+ # hosted on a public site.
+ test_config_global protocol.file.allow always &&
+
+ # Make sure that Git tries to use symlinks on Windows
+ test_config_global core.symlinks true &&
+
+ tell_tale_path="$PWD/tell.tale" &&
+ git init hook &&
+ (
+ cd hook &&
+ mkdir -p y/hooks &&
+ write_script y/hooks/post-checkout <<-EOF &&
+ echo HOOK-RUN >&2
+ echo hook-run >"$tell_tale_path"
+ EOF
+ git add y/hooks/post-checkout &&
+ test_tick &&
+ git commit -m post-checkout
+ ) &&
+
+ hook_repo_path="$(pwd)/hook" &&
+ git init captain &&
+ (
+ cd captain &&
+ git submodule add --name x/y "$hook_repo_path" A/modules/x &&
+ test_tick &&
+ git commit -m add-submodule &&
+
+ printf .git >dotgit.txt &&
+ git hash-object -w --stdin <dotgit.txt >dot-git.hash &&
+ printf "120000 %s 0\ta\n" "$(cat dot-git.hash)" >index.info &&
+ git update-index --index-info <index.info &&
+ test_tick &&
+ git commit -m add-symlink
+ ) &&
+
+ test_path_is_missing "$tell_tale_path" &&
+ test_must_fail git clone --recursive captain hooked 2>err &&
+ grep "directory not empty" err &&
+ test_path_is_missing "$tell_tale_path"
+'
+
add_submodule_commit_and_validate () {
HASH=$(git rev-parse HEAD) &&
git update-index --add --cacheinfo 160000,$HASH,sub &&
--
2.20.1

View File

@ -0,0 +1,153 @@
From f4aa8c8bb11dae6e769cd930565173808cbb69c8 Mon Sep 17 00:00:00 2001
From: Johannes Schindelin <johannes.schindelin@gmx.de>
Date: Wed, 10 Apr 2024 14:39:37 +0200
Subject: [PATCH] fetch/clone: detect dubious ownership of local repositories
When cloning from somebody else's repositories, it is possible that,
say, the `upload-pack` command is overridden in the repository that is
about to be cloned, which would then be run in the user's context who
started the clone.
To remind the user that this is a potentially unsafe operation, let's
extend the ownership checks we have already established for regular
gitdir discovery to extend also to local repositories that are about to
be cloned.
This protection extends also to file:// URLs.
The fixes in this commit address CVE-2024-32004.
Note: This commit does not touch the `fetch`/`clone` code directly, but
instead the function used implicitly by both: `enter_repo()`. This
function is also used by `git receive-pack` (i.e. pushes), by `git
upload-archive`, by `git daemon` and by `git http-backend`. In setups
that want to serve repositories owned by different users than the
account running the service, this will require `safe.*` settings to be
configured accordingly.
Also note: there are tiny time windows where a time-of-check-time-of-use
("TOCTOU") race is possible. The real solution to those would be to work
with `fstat()` and `openat()`. However, the latter function is not
available on Windows (and would have to be emulated with rather
expensive low-level `NtCreateFile()` calls), and the changes would be
quite extensive, for my taste too extensive for the little gain given
that embargoed releases need to pay extra attention to avoid introducing
inadvertent bugs.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
setup.h | 12 ++++++++++++
path.c | 2 ++
setup.c | 21 +++++++++++++++++++++
t/t0411-clone-from-partial.sh | 6 +++---
4 files changed, 38 insertions(+), 3 deletions(-)
diff --git a/setup.h b/setup.h
index fcf49706a..a46a3e4b6 100644
--- a/setup.h
+++ b/setup.h
@@ -41,6 +41,18 @@ const char *read_gitfile_gently(const char *path, int *return_error_code);
const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
+/*
+ * Check if a repository is safe and die if it is not, by verifying the
+ * ownership of the worktree (if any), the git directory, and the gitfile (if
+ * any).
+ *
+ * Exemptions for known-safe repositories can be added via `safe.directory`
+ * config settings; for non-bare repositories, their worktree needs to be
+ * added, for bare ones their git directory.
+ */
+void die_upon_dubious_ownership(const char *gitfile, const char *worktree,
+ const char *gitdir);
+
void setup_work_tree(void);
/*
diff --git a/path.c b/path.c
index 492e17ad1..d61f70e87 100644
--- a/path.c
+++ b/path.c
@@ -840,6 +840,7 @@ const char *enter_repo(const char *path, int strict)
if (!suffix[i])
return NULL;
gitfile = read_gitfile(used_path.buf);
+ die_upon_dubious_ownership(gitfile, NULL, used_path.buf);
if (gitfile) {
strbuf_reset(&used_path);
strbuf_addstr(&used_path, gitfile);
@@ -850,6 +851,7 @@ const char *enter_repo(const char *path, int strict)
}
else {
const char *gitfile = read_gitfile(path);
+ die_upon_dubious_ownership(gitfile, NULL, path);
if (gitfile)
path = gitfile;
if (chdir(path))
diff --git a/setup.c b/setup.c
index cefd5f63c..9d401ae4c 100644
--- a/setup.c
+++ b/setup.c
@@ -1165,6 +1165,27 @@ static int ensure_valid_ownership(const char *gitfile,
return data.is_safe;
}
+void die_upon_dubious_ownership(const char *gitfile, const char *worktree,
+ const char *gitdir)
+{
+ struct strbuf report = STRBUF_INIT, quoted = STRBUF_INIT;
+ const char *path;
+
+ if (ensure_valid_ownership(gitfile, worktree, gitdir, &report))
+ return;
+
+ strbuf_complete(&report, '\n');
+ path = gitfile ? gitfile : gitdir;
+ sq_quote_buf_pretty(&quoted, path);
+
+ die(_("detected dubious ownership in repository at '%s'\n"
+ "%s"
+ "To add an exception for this directory, call:\n"
+ "\n"
+ "\tgit config --global --add safe.directory %s"),
+ path, report.buf, quoted.buf);
+}
+
static int allowed_bare_repo_cb(const char *key, const char *value,
const struct config_context *ctx UNUSED,
void *d)
diff --git a/t/t0411-clone-from-partial.sh b/t/t0411-clone-from-partial.sh
index fb72a0a9f..eb3360dbc 100755
--- a/t/t0411-clone-from-partial.sh
+++ b/t/t0411-clone-from-partial.sh
@@ -23,7 +23,7 @@ test_expect_success 'create evil repo' '
>evil/.git/shallow
'
-test_expect_failure 'local clone must not fetch from promisor remote and execute script' '
+test_expect_success 'local clone must not fetch from promisor remote and execute script' '
rm -f script-executed &&
test_must_fail git clone \
--upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
@@ -32,7 +32,7 @@ test_expect_failure 'local clone must not fetch from promisor remote and execute
test_path_is_missing script-executed
'
-test_expect_failure 'clone from file://... must not fetch from promisor remote and execute script' '
+test_expect_success 'clone from file://... must not fetch from promisor remote and execute script' '
rm -f script-executed &&
test_must_fail git clone \
--upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
@@ -41,7 +41,7 @@ test_expect_failure 'clone from file://... must not fetch from promisor remote a
test_path_is_missing script-executed
'
-test_expect_failure 'fetch from file://... must not fetch from promisor remote and execute script' '
+test_expect_success 'fetch from file://... must not fetch from promisor remote and execute script' '
rm -f script-executed &&
test_must_fail git fetch \
--upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
--
2.33.0

View File

@ -0,0 +1,90 @@
From 5c5a4a1c05932378d259b1fdd9526cab971656a2 Mon Sep 17 00:00:00 2001
From: Filip Hejsek <filip.hejsek@gmail.com>
Date: Sun, 28 Jan 2024 04:29:33 +0100
Subject: [PATCH] t0411: add tests for cloning from partial repo
Cloning from a partial repository must not fetch missing objects into
the partial repository, because that can lead to arbitrary code
execution.
Add a couple of test cases, pretending to the `upload-pack` command (and
to that command only) that it is working on a repository owned by
someone else.
Helped-by: Jeff King <peff@peff.net>
Signed-off-by: Filip Hejsek <filip.hejsek@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
t/t0411-clone-from-partial.sh | 60 +++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
create mode 100755 t/t0411-clone-from-partial.sh
diff --git a/t/t0411-clone-from-partial.sh b/t/t0411-clone-from-partial.sh
new file mode 100755
index 000000000..fb72a0a9f
--- /dev/null
+++ b/t/t0411-clone-from-partial.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='check that local clone does not fetch from promisor remotes'
+
+. ./test-lib.sh
+
+test_expect_success 'create evil repo' '
+ git init tmp &&
+ test_commit -C tmp a &&
+ git -C tmp config uploadpack.allowfilter 1 &&
+ git clone --filter=blob:none --no-local --no-checkout tmp evil &&
+ rm -rf tmp &&
+
+ git -C evil config remote.origin.uploadpack \"\$TRASH_DIRECTORY/fake-upload-pack\" &&
+ write_script fake-upload-pack <<-\EOF &&
+ echo >&2 "fake-upload-pack running"
+ >"$TRASH_DIRECTORY/script-executed"
+ exit 1
+ EOF
+ export TRASH_DIRECTORY &&
+
+ # empty shallow file disables local clone optimization
+ >evil/.git/shallow
+'
+
+test_expect_failure 'local clone must not fetch from promisor remote and execute script' '
+ rm -f script-executed &&
+ test_must_fail git clone \
+ --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
+ evil clone1 2>err &&
+ ! grep "fake-upload-pack running" err &&
+ test_path_is_missing script-executed
+'
+
+test_expect_failure 'clone from file://... must not fetch from promisor remote and execute script' '
+ rm -f script-executed &&
+ test_must_fail git clone \
+ --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
+ "file://$(pwd)/evil" clone2 2>err &&
+ ! grep "fake-upload-pack running" err &&
+ test_path_is_missing script-executed
+'
+
+test_expect_failure 'fetch from file://... must not fetch from promisor remote and execute script' '
+ rm -f script-executed &&
+ test_must_fail git fetch \
+ --upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
+ "file://$(pwd)/evil" 2>err &&
+ ! grep "fake-upload-pack running" err &&
+ test_path_is_missing script-executed
+'
+
+test_expect_success 'pack-objects should fetch from promisor remote and execute script' '
+ rm -f script-executed &&
+ echo "HEAD" | test_must_fail git -C evil pack-objects --revs --stdout >/dev/null 2>err &&
+ grep "fake-upload-pack running" err &&
+ test_path_is_file script-executed
+'
+
+test_done
--
2.33.0

View File

@ -0,0 +1,109 @@
From 1204e1a824c34071019fe106348eaa6d88f9528d Mon Sep 17 00:00:00 2001
From: Patrick Steinhardt <ps@pks.im>
Date: Mon, 15 Apr 2024 13:30:41 +0200
Subject: [PATCH] builtin/clone: refuse local clones of unsafe repositories
When performing a local clone of a repository we end up either copying
or hardlinking the source repository into the target repository. This is
significantly more performant than if we were to use git-upload-pack(1)
and git-fetch-pack(1) to create the new repository and preserves both
disk space and compute time.
Unfortunately though, performing such a local clone of a repository that
is not owned by the current user is inherently unsafe:
- It is possible that source files get swapped out underneath us while
we are copying or hardlinking them. While we do perform some checks
here to assert that we hardlinked the expected file, they cannot
reliably thwart time-of-check-time-of-use (TOCTOU) style races. It
is thus possible for an adversary to make us copy or hardlink
unexpected files into the target directory.
Ideally, we would address this by starting to use openat(3P),
fstatat(3P) and friends. Due to platform compatibility with Windows
we cannot easily do that though. Furthermore, the scope of these
fixes would likely be quite broad and thus not fit for an embargoed
security release.
- Even if we handled TOCTOU-style races perfectly, hardlinking files
owned by a different user into the target repository is not a good
idea in general. It is possible for an adversary to rewrite those
files to contain whatever data they want even after the clone has
completed.
Address these issues by completely refusing local clones of a repository
that is not owned by the current user. This reuses our existing infra we
have in place via `ensure_valid_ownership()` and thus allows a user to
override the safety guard by adding the source repository path to the
"safe.directory" configuration.
This addresses CVE-2024-32020.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
builtin/clone.c | 14 ++++++++++++++
t/t0033-safe-directory.sh | 24 ++++++++++++++++++++++++
2 files changed, 38 insertions(+)
diff --git a/builtin/clone.c b/builtin/clone.c
index 4b80fa087..9ec500d42 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -321,6 +321,20 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
struct dir_iterator *iter;
int iter_status;
+ /*
+ * Refuse copying directories by default which aren't owned by us. The
+ * code that performs either the copying or hardlinking is not prepared
+ * to handle various edge cases where an adversary may for example
+ * racily swap out files for symlinks. This can cause us to
+ * inadvertently use the wrong source file.
+ *
+ * Furthermore, even if we were prepared to handle such races safely,
+ * creating hardlinks across user boundaries is an inherently unsafe
+ * operation as the hardlinked files can be rewritten at will by the
+ * potentially-untrusted user. We thus refuse to do so by default.
+ */
+ die_upon_dubious_ownership(NULL, NULL, src_repo);
+
mkdir_if_missing(dest->buf, 0777);
iter = dir_iterator_begin(src->buf, DIR_ITERATOR_PEDANTIC);
diff --git a/t/t0033-safe-directory.sh b/t/t0033-safe-directory.sh
index dc3496897..11c3e8f28 100755
--- a/t/t0033-safe-directory.sh
+++ b/t/t0033-safe-directory.sh
@@ -80,4 +80,28 @@ test_expect_success 'safe.directory in included file' '
git status
'
+test_expect_success 'local clone of unowned repo refused in unsafe directory' '
+ test_when_finished "rm -rf source" &&
+ git init source &&
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ test_commit -C source initial
+ ) &&
+ test_must_fail git clone --local source target &&
+ test_path_is_missing target
+'
+
+test_expect_success 'local clone of unowned repo accepted in safe directory' '
+ test_when_finished "rm -rf source" &&
+ git init source &&
+ (
+ sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
+ test_commit -C source initial
+ ) &&
+ test_must_fail git clone --local source target &&
+ git config --global --add safe.directory "$(pwd)/source/.git" &&
+ git clone --local source target &&
+ test_path_is_dir target
+'
+
test_done
--
2.33.0

View File

@ -0,0 +1,60 @@
From d1bb66a546b4bb46005d17ba711caaad26f26c1e Mon Sep 17 00:00:00 2001
From: Patrick Steinhardt <ps@pks.im>
Date: Mon, 15 Apr 2024 13:30:31 +0200
Subject: [PATCH] builtin/clone: abort when hardlinked source and target file
differ
When performing local clones with hardlinks we refuse to copy source
files which are symlinks as a mitigation for CVE-2022-39253. This check
can be raced by an adversary though by changing the file to a symlink
after we have checked it.
Fix the issue by checking whether the hardlinked destination file
matches the source file and abort in case it doesn't.
This addresses CVE-2024-32021.
Reported-by: Apple Product Security <product-security@apple.com>
Suggested-by: Linus Torvalds <torvalds@linuxfoundation.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
builtin/clone.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/builtin/clone.c b/builtin/clone.c
index 073e6323d..4b80fa087 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -357,8 +357,27 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
if (unlink(dest->buf) && errno != ENOENT)
die_errno(_("failed to unlink '%s'"), dest->buf);
if (!option_no_hardlinks) {
- if (!link(src->buf, dest->buf))
+ if (!link(src->buf, dest->buf)) {
+ struct stat st;
+
+ /*
+ * Sanity-check whether the created hardlink
+ * actually links to the expected file now. This
+ * catches time-of-check-time-of-use bugs in
+ * case the source file was meanwhile swapped.
+ */
+ if (lstat(dest->buf, &st))
+ die(_("hardlink cannot be checked at '%s'"), dest->buf);
+ if (st.st_mode != iter->st.st_mode ||
+ st.st_ino != iter->st.st_ino ||
+ st.st_dev != iter->st.st_dev ||
+ st.st_size != iter->st.st_size ||
+ st.st_uid != iter->st.st_uid ||
+ st.st_gid != iter->st.st_gid)
+ die(_("hardlink different from source at '%s'"), dest->buf);
+
continue;
+ }
if (option_local > 0)
die_errno(_("failed to create link '%s'"), dest->buf);
option_no_hardlinks = 1;
--
2.33.0

View File

@ -0,0 +1,84 @@
From 150e6b0aedf57d224c3c49038c306477fa159886 Mon Sep 17 00:00:00 2001
From: Patrick Steinhardt <ps@pks.im>
Date: Mon, 15 Apr 2024 13:30:26 +0200
Subject: [PATCH] builtin/clone: stop resolving symlinks when copying files
When a user performs a local clone without `--no-local`, then we end up
copying the source repository into the target repository directly. To
optimize this even further, we try to hardlink files into place instead
of copying data over, which helps both disk usage and speed.
There is an important edge case in this context though, namely when we
try to hardlink symlinks from the source repository into the target
repository. Depending on both platform and filesystem the resulting
behaviour here can be different:
- On macOS and NetBSD, calling link(3P) with a symlink target creates
a hardlink to the file pointed to by the symlink.
- On Linux, calling link(3P) instead creates a hardlink to the symlink
itself.
To unify this behaviour, 36596fd2df (clone: better handle symlinked
files at .git/objects/, 2019-07-10) introduced logic to resolve symlinks
before we try to link(3P) files. Consequently, the new behaviour was to
always create a hard link to the target of the symlink on all platforms.
Eventually though, we figured out that following symlinks like this can
cause havoc when performing a local clone of a malicious repository,
which resulted in CVE-2022-39253. This issue was fixed via 6f054f9fb3
(builtin/clone.c: disallow `--local` clones with symlinks, 2022-07-28),
by refusing symlinks in the source repository.
But even though we now shouldn't ever link symlinks anymore, the code
that resolves symlinks still exists. In the best case the code does not
end up doing anything because there are no symlinks anymore. In the
worst case though this can be abused by an adversary that rewrites the
source file after it has been checked not to be a symlink such that it
actually is a symlink when we call link(3P). Thus, it is still possible
to recreate CVE-2022-39253 due to this time-of-check-time-of-use bug.
Remove the call to `realpath()`. This doesn't yet address the actual
vulnerability, which will be handled in a subsequent commit.
Reported-by: Apple Product Security <product-security@apple.com>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
builtin/clone.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/builtin/clone.c b/builtin/clone.c
index 3c2ae31a5..073e6323d 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -320,7 +320,6 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
int src_len, dest_len;
struct dir_iterator *iter;
int iter_status;
- struct strbuf realpath = STRBUF_INIT;
mkdir_if_missing(dest->buf, 0777);
@@ -358,8 +357,7 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
if (unlink(dest->buf) && errno != ENOENT)
die_errno(_("failed to unlink '%s'"), dest->buf);
if (!option_no_hardlinks) {
- strbuf_realpath(&realpath, src->buf, 1);
- if (!link(realpath.buf, dest->buf))
+ if (!link(src->buf, dest->buf))
continue;
if (option_local > 0)
die_errno(_("failed to create link '%s'"), dest->buf);
@@ -373,8 +371,6 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest,
strbuf_setlen(src, src_len);
die(_("failed to iterate over '%s'"), src->buf);
}
-
- strbuf_release(&realpath);
}
static void clone_local(const char *src_repo, const char *dest_repo)
--
2.33.0

View File

@ -0,0 +1,201 @@
From 7b70e9efb18c2cc3f219af399bd384c5801ba1d7 Mon Sep 17 00:00:00 2001
From: Jeff King <peff@peff.net>
Date: Tue, 16 Apr 2024 04:35:33 -0400
Subject: [PATCH] upload-pack: disable lazy-fetching by default
The upload-pack command tries to avoid trusting the repository in which
it's run (e.g., by not running any hooks and not using any config that
contains arbitrary commands). But if the server side of a fetch or a
clone is a partial clone, then either upload-pack or its child
pack-objects may run a lazy "git fetch" under the hood. And it is very
easy to convince fetch to run arbitrary commands.
The "server" side can be a local repository owned by someone else, who
would be able to configure commands that are run during a clone with the
current user's permissions. This issue has been designated
CVE-2024-32004.
The fix in this commit's parent helps in this scenario, as well as in
related scenarios using SSH to clone, where the untrusted .git directory
is owned by a different user id. But if you received one as a zip file,
on a USB stick, etc, it may be owned by your user but still untrusted.
This has been designated CVE-2024-32465.
To mitigate the issue more completely, let's disable lazy fetching
entirely during `upload-pack`. While fetching from a partial repository
should be relatively rare, it is certainly not an unreasonable workflow.
And thus we need to provide an escape hatch.
This commit works by respecting a GIT_NO_LAZY_FETCH environment variable
(to skip the lazy-fetch), and setting it in upload-pack, but only when
the user has not already done so (which gives us the escape hatch).
The name of the variable is specifically chosen to match what has
already been added in 'master' via e6d5479e7a (git: extend
--no-lazy-fetch to work across subprocesses, 2024-02-27). Since we're
building this fix as a backport for older versions, we could cherry-pick
that patch and its earlier steps. However, we don't really need the
niceties (like a "--no-lazy-fetch" option) that it offers. By using the
same name, everything should just work when the two are eventually
merged, but here are a few notes:
- the blocking of the fetch in e6d5479e7a is incomplete! It sets
fetch_if_missing to 0 when we setup the repository variable, but
that isn't enough. pack-objects in particular will call
prefetch_to_pack() even if that variable is 0. This patch by
contrast checks the environment variable at the lowest level before
we call the lazy fetch, where we can be sure to catch all code
paths.
Possibly the setting of fetch_if_missing from e6d5479e7a can be
reverted, but it may be useful to have. For example, some code may
want to use that flag to change behavior before it gets to the point
of trying to start the fetch. At any rate, that's all outside the
scope of this patch.
- there's documentation for GIT_NO_LAZY_FETCH in e6d5479e7a. We can
live without that here, because for the most part the user shouldn't
need to set it themselves. The exception is if they do want to
override upload-pack's default, and that requires a separate
documentation section (which is added here)
- it would be nice to use the NO_LAZY_FETCH_ENVIRONMENT macro added by
e6d5479e7a, but those definitions have moved from cache.h to
environment.h between 2.39.3 and master. I just used the raw string
literals, and we can replace them with the macro once this topic is
merged to master.
At least with respect to CVE-2024-32004, this does render this commit's
parent commit somewhat redundant. However, it is worth retaining that
commit as defense in depth, and because it may help other issues (e.g.,
symlink/hardlink TOCTOU races, where zip files are not really an
interesting attack vector).
The tests in t0411 still pass, but now we have _two_ mechanisms ensuring
that the evil command is not run. Let's beef up the existing ones to
check that they failed for the expected reason, that we refused to run
upload-pack at all with an alternate user id. And add two new ones for
the same-user case that both the restriction and its escape hatch.
Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
Documentation/git-upload-pack.txt | 16 ++++++++++++++++
builtin/upload-pack.c | 2 ++
promisor-remote.c | 10 ++++++++++
t/t0411-clone-from-partial.sh | 18 ++++++++++++++++++
4 files changed, 46 insertions(+)
diff --git a/Documentation/git-upload-pack.txt b/Documentation/git-upload-pack.txt
index b656b4756..fc4c62d7b 100644
--- a/Documentation/git-upload-pack.txt
+++ b/Documentation/git-upload-pack.txt
@@ -55,6 +55,22 @@ ENVIRONMENT
admins may need to configure some transports to allow this
variable to be passed. See the discussion in linkgit:git[1].
+`GIT_NO_LAZY_FETCH`::
+ When cloning or fetching from a partial repository (i.e., one
+ itself cloned with `--filter`), the server-side `upload-pack`
+ may need to fetch extra objects from its upstream in order to
+ complete the request. By default, `upload-pack` will refuse to
+ perform such a lazy fetch, because `git fetch` may run arbitrary
+ commands specified in configuration and hooks of the source
+ repository (and `upload-pack` tries to be safe to run even in
+ untrusted `.git` directories).
++
+This is implemented by having `upload-pack` internally set the
+`GIT_NO_LAZY_FETCH` variable to `1`. If you want to override it
+(because you are fetching from a partial clone, and you are sure
+you trust it), you can explicitly set `GIT_NO_LAZY_FETCH` to
+`0`.
+
SEE ALSO
--------
linkgit:gitnamespaces[7]
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index 25b69da2b..f446ff04f 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -35,6 +35,8 @@ int cmd_upload_pack(int argc, const char **argv, const char *prefix)
packet_trace_identity("upload-pack");
disable_replace_refs();
+ /* TODO: This should use NO_LAZY_FETCH_ENVIRONMENT */
+ xsetenv("GIT_NO_LAZY_FETCH", "1", 0);
argc = parse_options(argc, argv, prefix, options, upload_pack_usage, 0);
diff --git a/promisor-remote.c b/promisor-remote.c
index faa761294..550a38f75 100644
--- a/promisor-remote.c
+++ b/promisor-remote.c
@@ -20,6 +20,16 @@ static int fetch_objects(struct repository *repo,
int i;
FILE *child_in;
+ /* TODO: This should use NO_LAZY_FETCH_ENVIRONMENT */
+ if (git_env_bool("GIT_NO_LAZY_FETCH", 0)) {
+ static int warning_shown;
+ if (!warning_shown) {
+ warning_shown = 1;
+ warning(_("lazy fetching disabled; some objects may not be available"));
+ }
+ return -1;
+ }
+
child.git_cmd = 1;
child.in = -1;
if (repo != the_repository)
diff --git a/t/t0411-clone-from-partial.sh b/t/t0411-clone-from-partial.sh
index eb3360dbc..b3d6ddc4b 100755
--- a/t/t0411-clone-from-partial.sh
+++ b/t/t0411-clone-from-partial.sh
@@ -28,6 +28,7 @@ test_expect_success 'local clone must not fetch from promisor remote and execute
test_must_fail git clone \
--upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
evil clone1 2>err &&
+ grep "detected dubious ownership" err &&
! grep "fake-upload-pack running" err &&
test_path_is_missing script-executed
'
@@ -37,6 +38,7 @@ test_expect_success 'clone from file://... must not fetch from promisor remote a
test_must_fail git clone \
--upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
"file://$(pwd)/evil" clone2 2>err &&
+ grep "detected dubious ownership" err &&
! grep "fake-upload-pack running" err &&
test_path_is_missing script-executed
'
@@ -46,6 +48,7 @@ test_expect_success 'fetch from file://... must not fetch from promisor remote a
test_must_fail git fetch \
--upload-pack="GIT_TEST_ASSUME_DIFFERENT_OWNER=true git-upload-pack" \
"file://$(pwd)/evil" 2>err &&
+ grep "detected dubious ownership" err &&
! grep "fake-upload-pack running" err &&
test_path_is_missing script-executed
'
@@ -57,4 +60,19 @@ test_expect_success 'pack-objects should fetch from promisor remote and execute
test_path_is_file script-executed
'
+test_expect_success 'clone from promisor remote does not lazy-fetch by default' '
+ rm -f script-executed &&
+ test_must_fail git clone evil no-lazy 2>err &&
+ grep "lazy fetching disabled" err &&
+ test_path_is_missing script-executed
+'
+
+test_expect_success 'promisor lazy-fetching can be re-enabled' '
+ rm -f script-executed &&
+ test_must_fail env GIT_NO_LAZY_FETCH=0 \
+ git clone evil lazy-ok 2>err &&
+ grep "fake-upload-pack running" err &&
+ test_path_is_file script-executed
+'
+
test_done
--
2.33.0

View File

@ -0,0 +1,116 @@
From 6ff658cc78f36baa74c0f25314b0043a8f4b4fc6 Mon Sep 17 00:00:00 2001
From: Todd Zullinger <tmz@pobox.com>
Date: Thu, 16 Nov 2023 14:30:11 -0500
Subject: [PATCH] send-email: avoid duplicate specification warnings
A warning is issued for options which are specified more than once
beginning with perl-Getopt-Long >= 2.55. In addition to causing users
to see warnings, this results in test failures which compare the output.
An example, from t9001-send-email.37:
| +++ diff -u expect actual
| --- expect 2023-11-14 10:38:23.854346488 +0000
| +++ actual 2023-11-14 10:38:23.848346466 +0000
| @@ -1,2 +1,7 @@
| +Duplicate specification "no-chain-reply-to" for option "no-chain-reply-to"
| +Duplicate specification "to-cover|to-cover!" for option "to-cover"
| +Duplicate specification "cc-cover|cc-cover!" for option "cc-cover"
| +Duplicate specification "no-thread" for option "no-thread"
| +Duplicate specification "no-to-cover" for option "no-to-cover"
| fatal: longline.patch:35 is longer than 998 characters
| warning: no patches were sent
| error: last command exited with $?=1
| not ok 37 - reject long lines
Remove the duplicate option specs. These are primarily the explicit
'--no-' prefix opts which were added in f471494303 (git-send-email.perl:
support no- prefix with older GetOptions, 2015-01-30). This was done
specifically to support perl-5.8.0 which includes Getopt::Long 2.32[1].
Getopt::Long 2.33 added support for the '--no-' prefix natively by
appending '!' to the option specification string, which was included in
perl-5.8.1 and is not present in perl-5.8.0. The previous commit bumped
the minimum supported Perl version to 5.8.1 so we no longer need to
provide the '--no-' variants for negatable options manually.
Teach `--git-completion-helper` to output the '--no-' options. They are
not included in the options hash and would otherwise be lost.
Signed-off-by: Todd Zullinger <tmz@pobox.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
git-send-email.perl | 19 ++++++-------------
1 file changed, 6 insertions(+), 13 deletions(-)
diff --git a/git-send-email.perl b/git-send-email.perl
index 041db702d4..60afafb375 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -119,13 +119,16 @@ sub completion_helper {
foreach my $key (keys %$original_opts) {
unless (exists $not_for_completion{$key}) {
- $key =~ s/!$//;
+ my $negatable = ($key =~ s/!$//);
if ($key =~ /[:=][si]$/) {
$key =~ s/[:=][si]$//;
push (@send_email_opts, "--$_=") foreach (split (/\|/, $key));
} else {
push (@send_email_opts, "--$_") foreach (split (/\|/, $key));
+ if ($negatable) {
+ push (@send_email_opts, "--no-$_") foreach (split (/\|/, $key));
+ }
}
}
}
@@ -491,7 +494,6 @@ sub config_regexp {
"bcc=s" => \@getopt_bcc,
"no-bcc" => \$no_bcc,
"chain-reply-to!" => \$chain_reply_to,
- "no-chain-reply-to" => sub {$chain_reply_to = 0},
"sendmail-cmd=s" => \$sendmail_cmd,
"smtp-server=s" => \$smtp_server,
"smtp-server-option=s" => \@smtp_server_options,
@@ -506,36 +508,27 @@ sub config_regexp {
"smtp-auth=s" => \$smtp_auth,
"no-smtp-auth" => sub {$smtp_auth = 'none'},
"annotate!" => \$annotate,
- "no-annotate" => sub {$annotate = 0},
"compose" => \$compose,
"quiet" => \$quiet,
"cc-cmd=s" => \$cc_cmd,
"header-cmd=s" => \$header_cmd,
"no-header-cmd" => \$no_header_cmd,
"suppress-from!" => \$suppress_from,
- "no-suppress-from" => sub {$suppress_from = 0},
"suppress-cc=s" => \@suppress_cc,
"signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,
- "no-signed-off-cc|no-signed-off-by-cc" => sub {$signed_off_by_cc = 0},
- "cc-cover|cc-cover!" => \$cover_cc,
- "no-cc-cover" => sub {$cover_cc = 0},
- "to-cover|to-cover!" => \$cover_to,
- "no-to-cover" => sub {$cover_to = 0},
+ "cc-cover!" => \$cover_cc,
+ "to-cover!" => \$cover_to,
"confirm=s" => \$confirm,
"dry-run" => \$dry_run,
"envelope-sender=s" => \$envelope_sender,
"thread!" => \$thread,
- "no-thread" => sub {$thread = 0},
"validate!" => \$validate,
- "no-validate" => sub {$validate = 0},
"transfer-encoding=s" => \$target_xfer_encoding,
"format-patch!" => \$format_patch,
- "no-format-patch" => sub {$format_patch = 0},
"8bit-encoding=s" => \$auto_8bit_encoding,
"compose-encoding=s" => \$compose_encoding,
"force" => \$force,
"xmailer!" => \$use_xmailer,
- "no-xmailer" => sub {$use_xmailer = 0},
"batch-size=i" => \$batch_size,
"relogin-delay=i" => \$relogin_delay,
"git-completion-helper" => \$git_completion_helper,
--
2.33.0

Binary file not shown.

Binary file not shown.

BIN
git-2.43.0.tar.sign Normal file

Binary file not shown.

BIN
git-2.43.0.tar.xz Normal file

Binary file not shown.

View File

@ -1,7 +1,7 @@
%global gitexecdir %{_libexecdir}/git-core
Name: git
Version: 2.39.1
Release: 8
Version: 2.43.0
Release: 4
Summary: A popular and widely used Version Control System
License: GPLv2+ or LGPLv2.1
URL: https://git-scm.com/
@ -12,19 +12,14 @@ Source100: git-gui.desktop
Source101: git@.service.in
Source102: git.socket
Patch0: backport-CVE-2022-41953-windows-ignore-empty-PATH-elements.patch
Patch1: backport-CVE-2022-41953-is_Cygwin-avoid-exec-ing-anything.patch
Patch2: backport-CVE-2022-41953-Move-is_-platform-functions-to-the-beginning.patch
Patch3: backport-CVE-2022-41953-Move-the-_which-function-almost-to-the-top.patch
Patch4: backport-CVE-2022-41953-Work-around-Tcl-s-default-PATH-lookup.patch
Patch5: backport-CVE-2023-22490-attr-adjust-a-mismatched-data-type.patch
Patch6: backport-CVE-2023-22490-t5619-demonstrate-clone_local-with-ambiguous-transpo.patch
Patch7: backport-CVE-2023-22490-clone-delay-picking-a-transport-until-after-get_repo.patch
Patch8: backport-CVE-2023-22490-dir-iterator-prevent-top-level-symlinks-without-FOLL.patch
Patch9: backport-CVE-2023-23946-apply-fix-writing-behind-newly-created-symbolic-link.patch
Patch10: backport-CVE-2023-25652-apply-reject-overwrite-existing-.rej-symlink-if-it-e.patch
Patch11: backport-CVE-2023-29007.patch
Patch12: backport-CVE-2023-25815-gettext-avoid-using-gettext-if-the-locale-dir-is-not.patch
Patch0: backport-send-email-avoid-duplicate-specification-warnings.patch
Patch1: backport-CVE-2024-32002-submodules-submodule-paths-m.patch
Patch2: backport-CVE-2024-32021-builtin-clone-stop-resolving-symlinks-when-copying-f.patch
Patch3: backport-CVE-2024-32021-builtin-clone-abort-when-hardlinked-source-and-targe.patch
Patch4: backport-CVE-2024-32004-t0411-add-tests-for-cloning-from-partial-repo.patch
Patch5: backport-CVE-2024-32004-fetch-clone-detect-dubious-ownership-of-local-reposi.patch
Patch6: backport-CVE-2024-32020-builtin-clone-refuse-local-clones-of-unsafe-reposito.patch
Patch7: backport-CVE-2024-32465-upload-pack-disable-lazy-fetching-by-default.patch
BuildRequires: gcc gettext
BuildRequires: openssl-devel libcurl-devel expat-devel systemd asciidoc xmlto glib2-devel libsecret-devel pcre2-devel desktop-file-utils
@ -257,7 +252,7 @@ make %{?_smp_mflags} test
%exclude %{_datadir}/git-core/templates/hooks/fsmonitor-watchman.sample
%exclude %{_datadir}/git-core/templates/hooks/pre-rebase.sample
%exclude %{_datadir}/git-core/templates/hooks/prepare-commit-msg.sample
%{_datadir}/locale/{bg,ca,de,el,es,fr,id,is,it,ko,pl,pt_PT,ru,sv,tr,vi,zh_CN,zh_TW}/LC_MESSAGES/git.mo
%{_datadir}/locale/{bg,ca,de,el,es,fr,id,is,it,ko,pl,pt_PT,ru,sv,uk,tr,vi,zh_CN,zh_TW}/LC_MESSAGES/git.mo
%{_datadir}/bash-completion/completions
%{_datadir}/git-core/
@ -309,6 +304,36 @@ make %{?_smp_mflags} test
%{_mandir}/man7/git*.7.*
%changelog
* Fri May 17 2024 fuanan <fuanan3@h-partners.com> - 2.43.0-4
- Type:CVE
- ID:CVE-2024-32021 CVE-2024-32004 CVE-2024-32020 CVE-2024-32465
- SUG:NA
- DESC:Fix CVE-2024-32021 CVE-2024-32004 CVE-2024-32020 CVE-2024-32465
* Wed May 15 2024 qiaojijun <qiaojijun@kylinos.cn> - 2.43.0-3
- Type:CVE
- ID:CVE-2024-32002
- SUG:NA
- DESC:Fix CVE-2024-32002
* Mon Apr 08 2024 fuanan <fuanan3@h-partners.com> - 2.43.0-2
- Type:bugfix
- ID:NA
- SUG:NA
- DESC:Fix t9001-send-email.sh test error
* Fri Dec 15 2023 fuanan <fuanan3@h-partners.com> - 2.43.0-1
- Type:enhancement
- ID:NA
- SUG:NA
- DESC:update version to 2.43.0
* Fri Jul 14 2023 fuanan <fuanan3@h-partners.com> - 2.41.0-1
- Type:enhancement
- ID:NA
- SUG:NA
- DESC:update version to 2.41.0
* Mon Jul 3 2023 huyubiao <huyubiao@huawei.com> - 2.39.1-8
- Type:bugfix
- ID:NA