summaryrefslogtreecommitdiffstats
path: root/scripts/headers_check.pl
blob: b6aec5e4365f9bf2b58c615fd3c7e591da5c20b6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/usr/bin/env perl
# SPDX-License-Identifier: GPL-2.0
#
# headers_check.pl execute a number of trivial consistency checks
#
# Usage: headers_check.pl dir arch [files...]
# dir:   dir to look for included files
# arch:  architecture
# files: list of files to check
#
# The script reads the supplied files line by line and:
#
# 1) for each include statement it checks if the
#    included file actually exists.
#    Only include files located in asm* and linux* are checked.
#    The rest are assumed to be system include files.
#
# 2) It is checked that prototypes does not use "extern"
#
# 3) Check for leaked CONFIG_ symbols

use warnings;
use strict;
use File::Basename;

my ($dir, $arch, @files) = @ARGV;

my $ret = 0;
my $line;
my $lineno = 0;
my $filename;

foreach my $file (@files) {
	$filename = $file;

	open(my $fh, '<', $filename)
		or die "$filename: $!\n";
	$lineno = 0;
	while ($line = <$fh>) {
		$lineno++;
		&check_include();
		&check_asm_types();
		&check_sizetypes();
		&check_declarations();
		# Dropped for now. Too much noise &check_config();
	}
	close $fh;
}
exit $ret;

sub check_include
{
	if ($line =~ m/^\s*#\s*include\s+<((asm|linux).*)>/) {
		my $inc = $1;
		my $found;
		$found = stat($dir . "/" . $inc);
		if (!$found) {
			$inc =~ s#asm/#asm-$arch/#;
			$found = stat($dir . "/" . $inc);
		}
		if (!$found) {
			printf STDERR "$filename:$lineno: included file '$inc' is not exported\n";
			$ret = 1;
		}
	}
}

sub check_declarations
{
	# soundcard.h is what it is
	if ($line =~ m/^void seqbuf_dump\(void\);/) {
		return;
	}
	# drm headers are being C++ friendly
	if ($line =~ m/^extern "C"/) {
		return;
	}
	if ($line =~ m/^(\s*extern|unsigned|char|short|int|long|void)\b/) {
		printf STDERR "$filename:$lineno: " .
			      "userspace cannot reference function or " .
			      "variable defined in the kernel\n";
	}
}

sub check_config
{
	if ($line =~ m/[^a-zA-Z0-9_]+CONFIG_([a-zA-Z0-9_]+)[^a-zA-Z0-9_]/) {
		printf STDERR "$filename:$lineno: leaks CONFIG_$1 to userspace where it is not valid\n";
	}
}

my $linux_asm_types;
sub check_asm_types
{
	if ($filename =~ /types.h|int-l64.h|int-ll64.h/o) {
		return;
	}
	if ($lineno == 1) {
		$linux_asm_types = 0;
	} elsif ($linux_asm_types >= 1) {
		return;
	}
	if ($line =~ m/^\s*#\s*include\s+<asm\/types.h>/) {
		$linux_asm_types = 1;
		printf STDERR "$filename:$lineno: " .
		"include of <linux/types.h> is preferred over <asm/types.h>\n"
		# Warn until headers are all fixed
		#$ret = 1;
	}
}

my $linux_types;
my %import_stack = ();
sub check_include_typesh
{
	my $path = $_[0];
	my $import_path;

	my $fh;
	my @file_paths = ($path, $dir . "/" .  $path, dirname($filename) . "/" . $path);
	for my $possible ( @file_paths ) {
	    if (not $import_stack{$possible} and open($fh, '<', $possible)) {
		$import_path = $possible;
		$import_stack{$import_path} = 1;
		last;
	    }
	}
	if (eof $fh) {
	    return;
	}

	my $line;
	while ($line = <$fh>) {
		if ($line =~ m/^\s*#\s*include\s+<linux\/types.h>/) {
			$linux_types = 1;
			last;
		}
		if (my $included = ($line =~ /^\s*#\s*include\s+[<"](\S+)[>"]/)[0]) {
			check_include_typesh($included);
		}
	}
	close $fh;
	delete $import_stack{$import_path};
}

sub check_sizetypes
{
	if ($filename =~ /types.h|int-l64.h|int-ll64.h/o) {
		return;
	}
	if ($lineno == 1) {
		$linux_types = 0;
	} elsif ($linux_types >= 1) {
		return;
	}
	if ($line =~ m/^\s*#\s*include\s+<linux\/types.h>/) {
		$linux_types = 1;
		return;
	}
	if (my $included = ($line =~ /^\s*#\s*include\s+[<"](\S+)[>"]/)[0]) {
		check_include_typesh($included);
	}
	if ($line =~ m/__[us](8|16|32|64)\b/) {
		printf STDERR "$filename:$lineno: " .
		              "found __[us]{8,16,32,64} type " .
		              "without #include <linux/types.h>\n";
		$linux_types = 2;
		# Warn until headers are all fixed
		#$ret = 1;
	}
}