Revision 9c603e53d380459fb62fec7cd085acb0b74ac18f authored by Linus Torvalds on 08 September 2012, 19:57:30 UTC, committed by Linus Torvalds on 28 September 2012, 19:13:46 UTC
Sasha Levin has been running trinity in a KVM tools guest, and was able
to trigger the BUG_ON() at arch/x86/mm/pat.c:279 (verifying the range of
the memory type).  The call trace showed that it was mtdchar_mmap() that
created an invalid remap_pfn_range().

The problem is that mtdchar_mmap() does various really odd and subtle
things with the vma page offset etc, and uses the wrong types (and the
wrong overflow) detection for it.

For example, the page offset may well be 32-bit on a 32-bit
architecture, but after shifting it up by PAGE_SHIFT, we need to use a
potentially 64-bit resource_size_t to correctly hold the full value.

Also, we need to check that the vma length plus offset doesn't overflow
before we check that it is smaller than the length of the mtdmap region.

This fixes things up and tries to make the code a bit easier to read.

Reported-and-tested-by: Sasha Levin <levinsasha928@gmail.com>
Acked-by: Suresh Siddha <suresh.b.siddha@intel.com>
Acked-by: Artem Bityutskiy <dedekind1@gmail.com>
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: linux-mtd@lists.infradead.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 6672d90
Raw File
timeconst.pl
#!/usr/bin/perl
# -----------------------------------------------------------------------
#
#   Copyright 2007-2008 rPath, Inc. - All Rights Reserved
#
#   This file is part of the Linux kernel, and is made available under
#   the terms of the GNU General Public License version 2 or (at your
#   option) any later version; incorporated herein by reference.
#
# -----------------------------------------------------------------------
#

#
# Usage: timeconst.pl HZ > timeconst.h
#

# Precomputed values for systems without Math::BigInt
# Generated by:
# timeconst.pl --can 24 32 48 64 100 122 128 200 250 256 300 512 1000 1024 1200
%canned_values = (
	24 => [
		'0xa6aaaaab','0x2aaaaaa',26,
		125,3,
		'0xc49ba5e4','0x1fbe76c8b4',37,
		3,125,
		'0xa2c2aaab','0xaaaa',16,
		125000,3,
		'0xc9539b89','0x7fffbce4217d',47,
		3,125000,
	], 32 => [
		'0xfa000000','0x6000000',27,
		125,4,
		'0x83126e98','0xfdf3b645a',36,
		4,125,
		'0xf4240000','0x0',17,
		31250,1,
		'0x8637bd06','0x3fff79c842fa',46,
		1,31250,
	], 48 => [
		'0xa6aaaaab','0x6aaaaaa',27,
		125,6,
		'0xc49ba5e4','0xfdf3b645a',36,
		6,125,
		'0xa2c2aaab','0x15555',17,
		62500,3,
		'0xc9539b89','0x3fffbce4217d',46,
		3,62500,
	], 64 => [
		'0xfa000000','0xe000000',28,
		125,8,
		'0x83126e98','0x7ef9db22d',35,
		8,125,
		'0xf4240000','0x0',18,
		15625,1,
		'0x8637bd06','0x1fff79c842fa',45,
		1,15625,
	], 100 => [
		'0xa0000000','0x0',28,
		10,1,
		'0xcccccccd','0x733333333',35,
		1,10,
		'0x9c400000','0x0',18,
		10000,1,
		'0xd1b71759','0x1fff2e48e8a7',45,
		1,10000,
	], 122 => [
		'0x8325c53f','0xfbcda3a',28,
		500,61,
		'0xf9db22d1','0x7fbe76c8b',35,
		61,500,
		'0x8012e2a0','0x3ef36',18,
		500000,61,
		'0xffda4053','0x1ffffbce4217',45,
		61,500000,
	], 128 => [
		'0xfa000000','0x1e000000',29,
		125,16,
		'0x83126e98','0x3f7ced916',34,
		16,125,
		'0xf4240000','0x40000',19,
		15625,2,
		'0x8637bd06','0xfffbce4217d',44,
		2,15625,
	], 200 => [
		'0xa0000000','0x0',29,
		5,1,
		'0xcccccccd','0x333333333',34,
		1,5,
		'0x9c400000','0x0',19,
		5000,1,
		'0xd1b71759','0xfff2e48e8a7',44,
		1,5000,
	], 250 => [
		'0x80000000','0x0',29,
		4,1,
		'0x80000000','0x180000000',33,
		1,4,
		'0xfa000000','0x0',20,
		4000,1,
		'0x83126e98','0x7ff7ced9168',43,
		1,4000,
	], 256 => [
		'0xfa000000','0x3e000000',30,
		125,32,
		'0x83126e98','0x1fbe76c8b',33,
		32,125,
		'0xf4240000','0xc0000',20,
		15625,4,
		'0x8637bd06','0x7ffde7210be',43,
		4,15625,
	], 300 => [
		'0xd5555556','0x2aaaaaaa',30,
		10,3,
		'0x9999999a','0x1cccccccc',33,
		3,10,
		'0xd0555556','0xaaaaa',20,
		10000,3,
		'0x9d495183','0x7ffcb923a29',43,
		3,10000,
	], 512 => [
		'0xfa000000','0x7e000000',31,
		125,64,
		'0x83126e98','0xfdf3b645',32,
		64,125,
		'0xf4240000','0x1c0000',21,
		15625,8,
		'0x8637bd06','0x3ffef39085f',42,
		8,15625,
	], 1000 => [
		'0x80000000','0x0',31,
		1,1,
		'0x80000000','0x0',31,
		1,1,
		'0xfa000000','0x0',22,
		1000,1,
		'0x83126e98','0x1ff7ced9168',41,
		1,1000,
	], 1024 => [
		'0xfa000000','0xfe000000',32,
		125,128,
		'0x83126e98','0x7ef9db22',31,
		128,125,
		'0xf4240000','0x3c0000',22,
		15625,16,
		'0x8637bd06','0x1fff79c842f',41,
		16,15625,
	], 1200 => [
		'0xd5555556','0xd5555555',32,
		5,6,
		'0x9999999a','0x66666666',31,
		6,5,
		'0xd0555556','0x2aaaaa',22,
		2500,3,
		'0x9d495183','0x1ffcb923a29',41,
		3,2500,
	]
);

$has_bigint = eval 'use Math::BigInt qw(bgcd); 1;';

sub bint($)
{
	my($x) = @_;
	return Math::BigInt->new($x);
}

#
# Constants for division by reciprocal multiplication.
# (bits, numerator, denominator)
#
sub fmul($$$)
{
	my ($b,$n,$d) = @_;

	$n = bint($n);
	$d = bint($d);

	return scalar (($n << $b)+$d-bint(1))/$d;
}

sub fadj($$$)
{
	my($b,$n,$d) = @_;

	$n = bint($n);
	$d = bint($d);

	$d = $d/bgcd($n, $d);
	return scalar (($d-bint(1)) << $b)/$d;
}

sub fmuls($$$) {
	my($b,$n,$d) = @_;
	my($s,$m);
	my($thres) = bint(1) << ($b-1);

	$n = bint($n);
	$d = bint($d);

	for ($s = 0; 1; $s++) {
		$m = fmul($s,$n,$d);
		return $s if ($m >= $thres);
	}
	return 0;
}

# Generate a hex value if the result fits in 64 bits;
# otherwise skip.
sub bignum_hex($) {
	my($x) = @_;
	my $s = $x->as_hex();

	return (length($s) > 18) ? undef : $s;
}

# Provides mul, adj, and shr factors for a specific
# (bit, time, hz) combination
sub muladj($$$) {
	my($b, $t, $hz) = @_;
	my $s = fmuls($b, $t, $hz);
	my $m = fmul($s, $t, $hz);
	my $a = fadj($s, $t, $hz);
	return (bignum_hex($m), bignum_hex($a), $s);
}

# Provides numerator, denominator values
sub numden($$) {
	my($n, $d) = @_;
	my $g = bgcd($n, $d);
	return ($n/$g, $d/$g);
}

# All values for a specific (time, hz) combo
sub conversions($$) {
	my ($t, $hz) = @_;
	my @val = ();

	# HZ_TO_xx
	push(@val, muladj(32, $t, $hz));
	push(@val, numden($t, $hz));

	# xx_TO_HZ
	push(@val, muladj(32, $hz, $t));
	push(@val, numden($hz, $t));

	return @val;
}

sub compute_values($) {
	my($hz) = @_;
	my @val = ();
	my $s, $m, $a, $g;

	if (!$has_bigint) {
		die "$0: HZ == $hz not canned and ".
		    "Math::BigInt not available\n";
	}

	# MSEC conversions
	push(@val, conversions(1000, $hz));

	# USEC conversions
	push(@val, conversions(1000000, $hz));

	return @val;
}

sub outputval($$)
{
	my($name, $val) = @_;
	my $csuf;

	if (defined($val)) {
	    if ($name !~ /SHR/) {
		$val = "U64_C($val)";
	    }
	    printf "#define %-23s %s\n", $name.$csuf, $val.$csuf;
	}
}

sub output($@)
{
	my($hz, @val) = @_;
	my $pfx, $bit, $suf, $s, $m, $a;

	print "/* Automatically generated by kernel/timeconst.pl */\n";
	print "/* Conversion constants for HZ == $hz */\n";
	print "\n";
	print "#ifndef KERNEL_TIMECONST_H\n";
	print "#define KERNEL_TIMECONST_H\n";
	print "\n";

	print "#include <linux/param.h>\n";
	print "#include <linux/types.h>\n";

	print "\n";
	print "#if HZ != $hz\n";
	print "#error \"kernel/timeconst.h has the wrong HZ value!\"\n";
	print "#endif\n";
	print "\n";

	foreach $pfx ('HZ_TO_MSEC','MSEC_TO_HZ',
		      'HZ_TO_USEC','USEC_TO_HZ') {
		foreach $bit (32) {
			foreach $suf ('MUL', 'ADJ', 'SHR') {
				outputval("${pfx}_$suf$bit", shift(@val));
			}
		}
		foreach $suf ('NUM', 'DEN') {
			outputval("${pfx}_$suf", shift(@val));
		}
	}

	print "\n";
	print "#endif /* KERNEL_TIMECONST_H */\n";
}

# Pretty-print Perl values
sub perlvals(@) {
	my $v;
	my @l = ();

	foreach $v (@_) {
		if (!defined($v)) {
			push(@l, 'undef');
		} elsif ($v =~ /^0x/) {
			push(@l, "\'".$v."\'");
		} else {
			push(@l, $v.'');
		}
	}
	return join(',', @l);
}

($hz) = @ARGV;

# Use this to generate the %canned_values structure
if ($hz eq '--can') {
	shift(@ARGV);
	@hzlist = sort {$a <=> $b} (@ARGV);

	print "# Precomputed values for systems without Math::BigInt\n";
	print "# Generated by:\n";
	print "# timeconst.pl --can ", join(' ', @hzlist), "\n";
	print "\%canned_values = (\n";
	my $pf = "\t";
	foreach $hz (@hzlist) {
		my @values = compute_values($hz);
		print "$pf$hz => [\n";
		while (scalar(@values)) {
			my $bit;
			foreach $bit (32) {
				my $m = shift(@values);
				my $a = shift(@values);
				my $s = shift(@values);
				print "\t\t", perlvals($m,$a,$s), ",\n";
			}
			my $n = shift(@values);
			my $d = shift(@values);
			print "\t\t", perlvals($n,$d), ",\n";
		}
		print "\t]";
		$pf = ', ';
	}
	print "\n);\n";
} else {
	$hz += 0;			# Force to number
	if ($hz < 1) {
		die "Usage: $0 HZ\n";
	}

	@val = @{$canned_values{$hz}};
	if (!defined(@val)) {
		@val = compute_values($hz);
	}
	output($hz, @val);
}
exit 0;
back to top