Skip to content

Commit deadbee

Browse files
author
Karl Ostmo
committed
Apply collision technique from Jeff King
See http://thread.gmane.org/gmane.comp.version-control.git/183939/focus=184004 p3�X`
1 parent 6c1249c commit deadbee

File tree

3 files changed

+251
-7
lines changed

3 files changed

+251
-7
lines changed

builtin/commit.c

+54-2
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ static int show_ignored_in_status, have_option_m;
139139
static const char *only_include_assumed;
140140
static struct strbuf message = STRBUF_INIT;
141141

142+
static int collide;
143+
static unsigned char collide_sha1[20];
144+
static unsigned char collide_mask[20];
145+
146+
142147
static enum status_format {
143148
STATUS_FORMAT_NONE = 0,
144149
STATUS_FORMAT_LONG,
@@ -1565,6 +1570,51 @@ int run_commit_hook(int editor_is_used, const char *index_file, const char *name
15651570
return ret;
15661571
}
15671572

1573+
static int parse_partial_sha1(const char *s, unsigned char sha1[20])
1574+
{
1575+
unsigned int i;
1576+
1577+
hashclr(sha1);
1578+
1579+
for (i = 0; i < 40 && s[i]; i++) {
1580+
unsigned int v = hexval(s[i]);
1581+
if (v & ~0xf)
1582+
break;
1583+
if (!(i & 1))
1584+
v <<= 4;
1585+
sha1[i/2] |= v;
1586+
}
1587+
return i;
1588+
}
1589+
1590+
static void fill_sha1_mask(int n, unsigned char mask[20]) {
1591+
int i;
1592+
1593+
hashclr(mask);
1594+
for (i = 0; i < n/2; i++)
1595+
mask[i] = 0xff;
1596+
if (n & 1)
1597+
mask[i] = 0xf0;
1598+
}
1599+
1600+
static int opt_parse_collide(const struct option *opt, const char *arg,
1601+
int unset)
1602+
{
1603+
if (unset)
1604+
collide = 0;
1605+
else {
1606+
int n = parse_partial_sha1(arg, collide_sha1);
1607+
if (!arg[n])
1608+
fill_sha1_mask(n, collide_mask);
1609+
else if (arg[n] == '/')
1610+
parse_partial_sha1(arg + n + 1, collide_mask);
1611+
else
1612+
die("invalid --collide sha1: %s", arg);
1613+
collide = 1;
1614+
}
1615+
return 0;
1616+
}
1617+
15681618
int cmd_commit(int argc, const char **argv, const char *prefix)
15691619
{
15701620
static struct wt_status s;
@@ -1587,6 +1637,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
15871637
OPT_BOOL('e', "edit", &edit_flag, N_("force edit of commit")),
15881638
OPT_STRING(0, "cleanup", &cleanup_arg, N_("default"), N_("how to strip spaces and #comments from message")),
15891639
OPT_BOOL(0, "status", &include_status, N_("include status in commit message template")),
1640+
OPT_CALLBACK(0, "collide", NULL, "sha1[/mask]", "choose commit sha1 like <sha1>", opt_parse_collide),
15901641
{ OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
15911642
N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
15921643
/* end commit message options */
@@ -1746,8 +1797,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
17461797
append_merge_tag_headers(parents, &tail);
17471798
}
17481799

1749-
if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1,
1750-
parents, sha1, author_ident.buf, sign_commit, extra)) {
1800+
if (commit_tree_collide(sb.buf, sb.len, active_cache_tree->sha1, parents, sha1,
1801+
author_ident.buf, sign_commit, extra,
1802+
collide ? collide_sha1 : NULL, collide_mask)) {
17511803
rollback_index_files();
17521804
die(_("failed to write commit object"));
17531805
}

commit.c

+186-5
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "commit-slab.h"
1212
#include "prio-queue.h"
1313
#include "sha1-lookup.h"
14+
#include "thread-utils.h"
1415

1516
static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
1617

@@ -1081,6 +1082,159 @@ struct commit_list *reduce_heads(struct commit_list *heads)
10811082
return result;
10821083
}
10831084

1085+
static inline int sha1_match_mask(const unsigned char *sha1,
1086+
const unsigned char *want,
1087+
const unsigned char *mask)
1088+
{
1089+
int i;
1090+
for (i = 0; i < 20; i++)
1091+
if ((want[i] & mask[i]) != (sha1[i] & mask[i]))
1092+
return 0;
1093+
return 1;
1094+
}
1095+
1096+
static unsigned long find_collision(const unsigned char *want,
1097+
const unsigned char *mask,
1098+
const git_SHA_CTX *base,
1099+
unsigned long start,
1100+
unsigned long end)
1101+
{
1102+
unsigned long lulz;
1103+
1104+
for (lulz = start; lulz < end; lulz++) {
1105+
git_SHA_CTX guess;
1106+
unsigned char sha1[20];
1107+
1108+
memcpy(&guess, base, sizeof(guess));
1109+
git_SHA1_Update(&guess, &lulz, sizeof(lulz));
1110+
git_SHA1_Final(sha1, &guess);
1111+
1112+
if (sha1_match_mask(sha1, want, mask))
1113+
return lulz;
1114+
}
1115+
return end;
1116+
}
1117+
1118+
#ifndef NO_PTHREADS
1119+
struct collision_thread_data {
1120+
const unsigned char *want;
1121+
const unsigned char *mask;
1122+
const git_SHA_CTX *base;
1123+
unsigned long start;
1124+
unsigned long end;
1125+
1126+
pthread_mutex_t *mutex;
1127+
pthread_cond_t *cond;
1128+
int *threads_alive;
1129+
unsigned long *answer;
1130+
};
1131+
1132+
static void *collision_thread(void *vdata)
1133+
{
1134+
struct collision_thread_data *d = vdata;
1135+
unsigned long r;
1136+
1137+
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
1138+
1139+
r = find_collision(d->want, d->mask, d->base,
1140+
d->start, d->end);
1141+
1142+
pthread_mutex_lock(d->mutex);
1143+
1144+
(*d->threads_alive)--;
1145+
if (r != d->end) {
1146+
*d->answer = r;
1147+
pthread_cond_signal(d->cond);
1148+
}
1149+
else {
1150+
/* If we failed to find it, and we're the last thread,
1151+
* wake up the parent so it can report failure. */
1152+
if (!*d->threads_alive)
1153+
pthread_cond_signal(d->cond);
1154+
}
1155+
1156+
pthread_mutex_unlock(d->mutex);
1157+
return NULL;
1158+
}
1159+
1160+
static unsigned long find_collision_threaded(int nthreads,
1161+
const unsigned char *want,
1162+
const unsigned char *mask,
1163+
const git_SHA_CTX *base)
1164+
{
1165+
int i;
1166+
pthread_t *threads;
1167+
struct collision_thread_data *data;
1168+
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
1169+
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
1170+
int threads_alive = nthreads;
1171+
unsigned long ret = ULONG_MAX;
1172+
1173+
threads = xmalloc(nthreads * sizeof(*threads));
1174+
data = xmalloc(nthreads * sizeof(*data));
1175+
1176+
pthread_mutex_lock(&mutex);
1177+
1178+
for (i = 0; i < nthreads; i++) {
1179+
data[i].want = want;
1180+
data[i].mask = mask;
1181+
data[i].base = base;
1182+
data[i].start = i * (ULONG_MAX / nthreads);
1183+
data[i].end = (i+1) * (ULONG_MAX / nthreads);
1184+
data[i].mutex = &mutex;
1185+
data[i].cond = &cond;
1186+
data[i].answer = &ret;
1187+
data[i].threads_alive = &threads_alive;
1188+
pthread_create(&threads[i], NULL, collision_thread, &data[i]);
1189+
}
1190+
1191+
pthread_cond_wait(&cond, &mutex);
1192+
1193+
for (i = 0; i < nthreads; i++) {
1194+
pthread_cancel(threads[i]);
1195+
pthread_join(threads[i], NULL);
1196+
}
1197+
1198+
free(threads);
1199+
free(data);
1200+
return ret;
1201+
}
1202+
#endif /* NO_PTHREADS */
1203+
1204+
1205+
static void collide_commit(struct strbuf *data,
1206+
const unsigned char *want,
1207+
const unsigned char *mask)
1208+
{
1209+
static const char terminator[] = { 0 };
1210+
char header[32];
1211+
int header_len;
1212+
unsigned long lulz;
1213+
git_SHA_CTX base;
1214+
1215+
header_len = snprintf(header, sizeof(header),
1216+
"commit %lu",
1217+
data->len + 1 + sizeof(lulz)) + 1;
1218+
git_SHA1_Init(&base);
1219+
git_SHA1_Update(&base, header, header_len);
1220+
git_SHA1_Update(&base, data->buf, data->len);
1221+
git_SHA1_Update(&base, terminator, sizeof(terminator));
1222+
1223+
#ifdef NO_PTHREADS
1224+
lulz = find_collision(want, mask, &base, 0, ULONG_MAX);
1225+
#else
1226+
lulz = find_collision_threaded(online_cpus(), want, mask, &base);
1227+
#endif /* NO_PTHREADS */
1228+
1229+
if (lulz != ULONG_MAX) {
1230+
strbuf_add(data, terminator, sizeof(terminator));
1231+
strbuf_add(data, &lulz, sizeof(lulz));
1232+
}
1233+
else
1234+
warning("sorry, I couldn't find a collision!");
1235+
}
1236+
1237+
10841238
static const char gpg_sig_header[] = "gpgsig";
10851239
static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1;
10861240

@@ -1515,12 +1669,35 @@ static const char commit_utf8_warn[] =
15151669
"You may want to amend it after fixing the message, or set the config\n"
15161670
"variable i18n.commitencoding to the encoding your project uses.\n";
15171671

1518-
int commit_tree_extended(const char *msg, size_t msg_len,
1519-
const unsigned char *tree,
1520-
struct commit_list *parents, unsigned char *ret,
1521-
const char *author, const char *sign_commit,
1522-
struct commit_extra_header *extra)
1672+
int commit_tree_extended(
1673+
const char *msg,
1674+
size_t msg_len,
1675+
const unsigned char *tree,
1676+
struct commit_list *parents,
1677+
unsigned char *ret,
1678+
const char *author,
1679+
const char *sign_commit,
1680+
struct commit_extra_header *extra)
15231681
{
1682+
1683+
return commit_tree_collide(
1684+
msg, msg_len, tree, parents, ret, author, sign_commit, extra,
1685+
NULL, NULL);
1686+
}
1687+
1688+
int commit_tree_collide(
1689+
const char *msg,
1690+
size_t msg_len,
1691+
const unsigned char *tree,
1692+
struct commit_list *parents,
1693+
unsigned char *ret,
1694+
const char *author,
1695+
const char *sign_commit,
1696+
struct commit_extra_header *extra,
1697+
const unsigned char *want,
1698+
const unsigned char *mask)
1699+
{
1700+
15241701
int result;
15251702
int encoding_is_utf8;
15261703
struct strbuf buffer;
@@ -1575,6 +1752,10 @@ int commit_tree_extended(const char *msg, size_t msg_len,
15751752
if (sign_commit && do_sign_commit(&buffer, sign_commit))
15761753
return -1;
15771754

1755+
if (want && mask)
1756+
collide_commit(&buffer, want, mask);
1757+
1758+
15781759
result = write_sha1_file(buffer.buf, buffer.len, commit_type, ret);
15791760
strbuf_release(&buffer);
15801761
return result;

commit.h

+11
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,17 @@ extern int commit_tree_extended(const char *msg, size_t msg_len,
324324
const char *author, const char *sign_commit,
325325
struct commit_extra_header *);
326326

327+
extern int commit_tree_collide(
328+
const char *msg,
329+
size_t msg_len,
330+
const unsigned char *tree,
331+
struct commit_list *parents, unsigned char *ret,
332+
const char *author,
333+
const char *sign_commit,
334+
struct commit_extra_header *extra,
335+
const unsigned char *sha1,
336+
const unsigned char *mask);
337+
327338
extern struct commit_extra_header *read_commit_extra_headers(struct commit *, const char **);
328339

329340
extern void free_commit_extra_headers(struct commit_extra_header *extra);

0 commit comments

Comments
 (0)