commit: sign commit after mutating buffer

The ensure_utf8 function can mutate the buffer to change its encoding, so we must call it before signing the buffer so that we do not invalidate the signature, which is made over raw bytes. Fix a bug which caused the compatibility code to not convert the compatibility buffer if the main buffer was invalid UTF-8. We expect both buffers to be valid UTF-8 or both invalid, since the only data that would differ between them would be hex object IDs, which are always valid UTF-8. Add a test for this case using 0xfe and 0xff, which are never valid in UTF-8. Reported-by: Kushal Das <kushal@sunet.se> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

brian m. carlson committed Apr 27, 2026 at 22:18 UTC 7735d7eee3a586181dc397afa5aa8f02e009833b
2 files changed +21 -4
commit.c
+11 -4
@@ -1726,6 +1726,7 @@ int commit_tree_extended(const char *msg, size_t msg_len,
1726 struct repository *r = the_repository;
1727 int result = 0;
1728 int encoding_is_utf8;
1729 + bool warned = false;
1730 struct strbuf buffer = STRBUF_INIT, compat_buffer = STRBUF_INIT;
1731 struct strbuf sig = STRBUF_INIT, compat_sig = STRBUF_INIT;
1732 struct object_id *parent_buf = NULL, *compat_oid = NULL;
@@ -1747,6 +1748,13 @@ int commit_tree_extended(const char *msg, size_t msg_len,
1748 oidcpy(&parent_buf[i++], &p->item->object.oid);
1749
1750 write_commit_tree(&buffer, msg, msg_len, tree, parent_buf, nparents, author, committer, extra);
1751 +
1752 + /* And check the encoding. */
1753 + if (encoding_is_utf8 && !ensure_utf8(&buffer)) {
1754 + fprintf(stderr, _(commit_utf8_warn));
1755 + warned = true;
1756 + }
1757 +
1758 if (sign_commit && sign_buffer(&buffer, &sig, sign_commit,
1759 SIGN_BUFFER_USE_DEFAULT_KEY)) {
1760 result = -1;
@@ -1780,6 +1788,9 @@ int commit_tree_extended(const char *msg, size_t msg_len,
1788 free_commit_extra_headers(compat_extra);
1789 free(mapped_parents);
1790
1791 + if (encoding_is_utf8 && !ensure_utf8(&compat_buffer) && !warned)
1792 + fprintf(stderr, _(commit_utf8_warn));
1793 +
1794 if (sign_commit && sign_buffer(&compat_buffer, &compat_sig,
1795 sign_commit,
1796 SIGN_BUFFER_USE_DEFAULT_KEY)) {
@@ -1818,10 +1829,6 @@ int commit_tree_extended(const char *msg, size_t msg_len,
1829 }
1830 }
1831
1821 - /* And check the encoding. */
1822 - if (encoding_is_utf8 && (!ensure_utf8(&buffer) || !ensure_utf8(&compat_buffer)))
1823 - fprintf(stderr, _(commit_utf8_warn));
1824 -
1832 if (r->compat_hash_algo) {
1833 hash_object_file(r->compat_hash_algo, compat_buffer.buf, compat_buffer.len,
1834 OBJ_COMMIT, &compat_oid_buf);
t/t7510-signed-commit.sh
+10
@@ -462,4 +462,14 @@ test_expect_success 'custom `gpg.program`' '
462 git commit -S --allow-empty -m signed-commit
463 '
464
465 +test_expect_success GPG 'commit verifies with non-UTF-8 commit message' '
466 + printf "I hate\\376\\377UTF-8\\n" >message &&
467 + echo unusual-message >file &&
468 + git add file &&
469 + test_tick && git commit -S -F message 2>err &&
470 + git verify-commit HEAD &&
471 + grep "commit message did not conform to UTF-8" err >lines &&
472 + test_line_count = 1 lines
473 +'
474 +
475 test_done