git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: "René Scharfe" <l.s.r@web.de>
To: Git List <git@vger.kernel.org>
Cc: Junio C Hamano <gitster@pobox.com>,
	Duy Nguyen <pclouds@gmail.com>, Jeff King <peff@peff.net>
Subject: [PATCH] dir: support platforms that require aligned reads
Date: Sun, 16 Jul 2017 14:17:37 +0200	[thread overview]
Message-ID: <d3db2984-f238-7166-affa-f1f7df566404@web.de> (raw)

The untracked cache is stored on disk by concatenating its memory
structures without any padding.  Consequently some of the structs are
not aligned at a particular boundary when the whole extension is read
back in one go.  That's only OK on platforms without strict alignment
requirements, or for byte-aligned data like strings or hash values.

Decode struct ondisk_untracked_cache carefully from the extension
blob by using explicit pointer arithmetic with offsets, avoiding
alignment issues.  Use char pointers for passing stat_data objects to
stat_data_from_disk(), and use memcpy(3) in that function to  get the
contents into a properly aligned struct, then perform the byte-order
adjustment in place there.

Found with Clang's UBSan.

Signed-off-by: Rene Scharfe <l.s.r@web.de>
---
Side note: The OS name is not enough for determining the layout of
struct ondisk_untracked_cache.  Different platforms can have
different int sizes and padding.  Adding the machine type could
help, but that would be a breaking change.  At that point we would
be better off defining a machine-independent format, no?

 dir.c | 50 +++++++++++++++++++++++++++-----------------------
 1 file changed, 27 insertions(+), 23 deletions(-)

diff --git a/dir.c b/dir.c
index ae6f5c9636..1c55dc3e36 100644
--- a/dir.c
+++ b/dir.c
@@ -2398,7 +2398,8 @@ struct ondisk_untracked_cache {
 	char exclude_per_dir[FLEX_ARRAY];
 };
 
-#define ouc_size(len) (offsetof(struct ondisk_untracked_cache, exclude_per_dir) + len + 1)
+#define ouc_offset(x) offsetof(struct ondisk_untracked_cache, x)
+#define ouc_size(len) (ouc_offset(exclude_per_dir) + len + 1)
 
 struct write_data {
 	int index;	   /* number of written untracked_cache_dir */
@@ -2560,17 +2561,18 @@ struct read_data {
 	const unsigned char *end;
 };
 
-static void stat_data_from_disk(struct stat_data *to, const struct stat_data *from)
+static void stat_data_from_disk(struct stat_data *to, const unsigned char *data)
 {
-	to->sd_ctime.sec  = get_be32(&from->sd_ctime.sec);
-	to->sd_ctime.nsec = get_be32(&from->sd_ctime.nsec);
-	to->sd_mtime.sec  = get_be32(&from->sd_mtime.sec);
-	to->sd_mtime.nsec = get_be32(&from->sd_mtime.nsec);
-	to->sd_dev	  = get_be32(&from->sd_dev);
-	to->sd_ino	  = get_be32(&from->sd_ino);
-	to->sd_uid	  = get_be32(&from->sd_uid);
-	to->sd_gid	  = get_be32(&from->sd_gid);
-	to->sd_size	  = get_be32(&from->sd_size);
+	memcpy(to, data, sizeof(*to));
+	to->sd_ctime.sec  = ntohl(to->sd_ctime.sec);
+	to->sd_ctime.nsec = ntohl(to->sd_ctime.nsec);
+	to->sd_mtime.sec  = ntohl(to->sd_mtime.sec);
+	to->sd_mtime.nsec = ntohl(to->sd_mtime.nsec);
+	to->sd_dev	  = ntohl(to->sd_dev);
+	to->sd_ino	  = ntohl(to->sd_ino);
+	to->sd_uid	  = ntohl(to->sd_uid);
+	to->sd_gid	  = ntohl(to->sd_gid);
+	to->sd_size	  = ntohl(to->sd_size);
 }
 
 static int read_one_dir(struct untracked_cache_dir **untracked_,
@@ -2645,7 +2647,7 @@ static void read_stat(size_t pos, void *cb)
 		rd->data = rd->end + 1;
 		return;
 	}
-	stat_data_from_disk(&ud->stat_data, (struct stat_data *)rd->data);
+	stat_data_from_disk(&ud->stat_data, rd->data);
 	rd->data += sizeof(struct stat_data);
 	ud->valid = 1;
 }
@@ -2663,22 +2665,22 @@ static void read_sha1(size_t pos, void *cb)
 }
 
 static void load_sha1_stat(struct sha1_stat *sha1_stat,
-			   const struct stat_data *stat,
+			   const unsigned char *data,
 			   const unsigned char *sha1)
 {
-	stat_data_from_disk(&sha1_stat->stat, stat);
+	stat_data_from_disk(&sha1_stat->stat, data);
 	hashcpy(sha1_stat->sha1, sha1);
 	sha1_stat->valid = 1;
 }
 
 struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
 {
-	const struct ondisk_untracked_cache *ouc;
 	struct untracked_cache *uc;
 	struct read_data rd;
 	const unsigned char *next = data, *end = (const unsigned char *)data + sz;
 	const char *ident;
 	int ident_len, len;
+	const char *exclude_per_dir;
 
 	if (sz <= 1 || end[-1] != '\0')
 		return NULL;
@@ -2690,21 +2692,23 @@ struct untracked_cache *read_untracked_extension(const void *data, unsigned long
 	ident = (const char *)next;
 	next += ident_len;
 
-	ouc = (const struct ondisk_untracked_cache *)next;
 	if (next + ouc_size(0) > end)
 		return NULL;
 
 	uc = xcalloc(1, sizeof(*uc));
 	strbuf_init(&uc->ident, ident_len);
 	strbuf_add(&uc->ident, ident, ident_len);
-	load_sha1_stat(&uc->ss_info_exclude, &ouc->info_exclude_stat,
-		       ouc->info_exclude_sha1);
-	load_sha1_stat(&uc->ss_excludes_file, &ouc->excludes_file_stat,
-		       ouc->excludes_file_sha1);
-	uc->dir_flags = get_be32(&ouc->dir_flags);
-	uc->exclude_per_dir = xstrdup(ouc->exclude_per_dir);
+	load_sha1_stat(&uc->ss_info_exclude,
+		       next + ouc_offset(info_exclude_stat),
+		       next + ouc_offset(info_exclude_sha1));
+	load_sha1_stat(&uc->ss_excludes_file,
+		       next + ouc_offset(excludes_file_stat),
+		       next + ouc_offset(excludes_file_sha1));
+	uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
+	exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir);
+	uc->exclude_per_dir = xstrdup(exclude_per_dir);
 	/* NUL after exclude_per_dir is covered by sizeof(*ouc) */
-	next += ouc_size(strlen(ouc->exclude_per_dir));
+	next += ouc_size(strlen(exclude_per_dir));
 	if (next >= end)
 		goto done2;
 
-- 
2.13.3

             reply	other threads:[~2017-07-16 12:17 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-07-16 12:17 René Scharfe [this message]
2017-07-16 14:04 ` [PATCH] dir: support platforms that require aligned reads Jeff King
2017-07-16 15:18   ` René Scharfe
2017-07-16 16:03     ` Jeff King

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d3db2984-f238-7166-affa-f1f7df566404@web.de \
    --to=l.s.r@web.de \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=pclouds@gmail.com \
    --cc=peff@peff.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).