Skip to content

Commit 9db0571

Browse files
committed
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 <[email protected]> Helped-by: Taylor Blau <[email protected]> Helped-by: Junio C Hamano <[email protected]> Helped-by: Linus Torvalds <[email protected]> Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 2f3b28f commit 9db0571

File tree

2 files changed

+27
-2
lines changed

2 files changed

+27
-2
lines changed

apply.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4558,7 +4558,7 @@ static int write_out_one_reject(struct apply_state *state, struct patch *patch)
45584558
FILE *rej;
45594559
char namebuf[PATH_MAX];
45604560
struct fragment *frag;
4561-
int cnt = 0;
4561+
int fd, cnt = 0;
45624562
struct strbuf sb = STRBUF_INIT;
45634563

45644564
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)
45984598
memcpy(namebuf, patch->new_name, cnt);
45994599
memcpy(namebuf + cnt, ".rej", 5);
46004600

4601-
rej = fopen(namebuf, "w");
4601+
fd = open(namebuf, O_CREAT | O_EXCL | O_WRONLY, 0666);
4602+
if (fd < 0) {
4603+
if (errno != EEXIST)
4604+
return error_errno(_("cannot open %s"), namebuf);
4605+
if (unlink(namebuf))
4606+
return error_errno(_("cannot unlink '%s'"), namebuf);
4607+
fd = open(namebuf, O_CREAT | O_EXCL | O_WRONLY, 0666);
4608+
if (fd < 0)
4609+
return error_errno(_("cannot open %s"), namebuf);
4610+
}
4611+
rej = fdopen(fd, "w");
46024612
if (!rej)
46034613
return error_errno(_("cannot open %s"), namebuf);
46044614

t/t4115-apply-symlink.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,19 @@ test_expect_success SYMLINKS 'symlink escape when deleting file' '
125125
test_path_is_file .git/delete-me
126126
'
127127

128+
test_expect_success SYMLINKS '--reject removes .rej symlink if it exists' '
129+
test_when_finished "git reset --hard && git clean -dfx" &&
130+
131+
test_commit file &&
132+
echo modified >file.t &&
133+
git diff -- file.t >patch &&
134+
echo modified-again >file.t &&
135+
136+
ln -s foo file.t.rej &&
137+
test_must_fail git apply patch --reject 2>err &&
138+
test_i18ngrep "Rejected hunk" err &&
139+
test_path_is_missing foo &&
140+
test_path_is_file file.t.rej
141+
'
142+
128143
test_done

0 commit comments

Comments
 (0)