Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 15 Jul 2002 14:49:48 +0200
From:      Thomas Moestl <tmoestl@gmx.net>
To:        Luigi Rizzo <luigi@FreeBSD.org>
Cc:        Mike Barcroft <mike@FreeBSD.org>, current@FreeBSD.org
Subject:   Re: different packing of structs in kernel vs. userland ?
Message-ID:  <20020715124947.GB314@crow.dom2ip.de>
In-Reply-To: <20020715040008.A85276@iguana.icir.org>
References:  <20020714011810.A72236@iguana.icir.org> <20020714203642.GD314@crow.dom2ip.de> <20020714230821.C64412@espresso.q9media.com> <20020715105158.GA314@crow.dom2ip.de> <20020715040008.A85276@iguana.icir.org>

next in thread | previous in thread | raw e-mail | index | archive | help
On Mon, 2002/07/15 at 04:00:08 -0700, Luigi Rizzo wrote:
> sorry but all this just does not make sense to me.
> 
> sizeof(foo) should give the same result irrespective of
> where you use it.

OK, let me rephrase it: as I explained before, struct ip_fw has padding
after 'cmd' (the last member) to ensure that arrays can be built from
it safely, so that the first member will always be properly
aligned.
Since the first members must/should be aligned on an 8-bit boundary on
64-bit platforms, this means that sizeof(struct ip_fw) must be a
multiple of 8, the size of the padding is 4 bytes (unless the
situation is changed by reordering structure members). This can easily
be checked on a 64-bit platform. The following program fragment:

	struct ip_fw f;

	printf("sizeof(ip_fw) = %d\n", (int)sizeof(f));
	printf("offsetof(ip_fw, cmd) = %d\n", (int)offsetof(struct ip_fw, cmd));
	printf("sizeof(ip_fw.cmd) = %d\n", (int)sizeof(f.cmd));

Produces this output on sparc64:

        sizeof(ip_fw) = 56
        offsetof(ip_fw, cmd) = 48
        sizeof(ip_fw.cmd) = 4

This illustrates that indeed, padding is appended after 'cmd'.

In the (userland) ipfw2.c, you basically do the following:

        ipfw_insn *dst; /* sizeof(ipfw_insn) = 4 */

	dst = (ipfw_insn *)rule->cmd;
	/* Write n instructions and increase dst accordingly. */
	rule->cmd_len = (u_int32_t *)dst - (u_int32_t *)(rule->cmd);
	i = (void *)dst - (void *)rule;

	if (getsockopt(s, IPPROTO_IP, IP_FW_ADD, rule, &i) == -1)
		err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");

Let's consider the case where only one instruction was added. In this
case, dst was incremented once and points directly after cmd, so i is
52 on a 64-bit platform.
However, sizeof(struct ip_fw) is 56 because the aformentioned 4 bytes
of padding following 'cmd', so i < sizeof(struct ip_fw). This explains
why rules with just one instruction would not work properly in this
case with just my first patch. 
Likewise, when adding more rules, the second one will be added to the
memory location directly following 'cmd'. If padding is present, the
second instruction will write into it. The size of the total structure
will thus not be properly computed by the old RULESIZE macro:

	#define RULESIZE(rule)  (sizeof(struct ip_fw) + \
	        ((struct ip_fw *)(rule))->cmd_len * 4 - 4)

The '- 4' is meant to subtract the size of the cmd, which is accounted
for in cmd_len. Still, you are counting the padding twice, once in the
sizeof() and once in cmd_len.

So, sizeof(struct ip_fw) is no different between userland and kernel,
but the problem is that you don't use sizeof(struct ip_fw) in userland
to compute the sizes (but pointer arithmetic), but you do use it for
the checks in the kernel.

	- thomas

-- 
Thomas Moestl <tmoestl@gmx.net>	http://www.tu-bs.de/~y0015675/
              <tmm@FreeBSD.org>	http://people.FreeBSD.org/~tmm/
PGP fingerprint: 1C97 A604 2BD0 E492 51D0  9C0F 1FE6 4F1D 419C 776C

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-current" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020715124947.GB314>