1#!/usr/bin/perl 2 3# Check for malloc calls not shortly followed by initialisation. 4# 5# Known limitations: 6# - false negative: can't see allocations spanning more than one line 7# - possible false negatives, see patterns 8# - false positive: malloc-malloc-init-init is not accepted 9# - false positives: "non-standard" init functions (eg, the things being 10# initialised is not the first arg, or initialise struct members) 11# 12# Since false positives are expected, the results must be manually reviewed. 13# 14# Typical usage: scripts/malloc-init.pl library/*.c 15 16use warnings; 17use strict; 18 19use utf8; 20use open qw(:std utf8); 21 22my $limit = 7; 23my $inits = qr/memset|memcpy|_init|fread|base64_..code/; 24 25# cases to bear in mind: 26# 27# 0. foo = malloc(...); memset( foo, ... ); 28# 1. *foo = malloc(...); memset( *foo, ... ); 29# 2. type *foo = malloc(...); memset( foo, ...); 30# 3. foo = malloc(...); foo_init( (type *) foo ); 31# 4. foo = malloc(...); for(i=0..n) { init( &foo[i] ); } 32# 33# The chosen patterns are a bit relaxed, but unlikely to cause false positives 34# in real code (initialising *foo or &foo instead of foo will likely be caught 35# by functional tests). 36# 37my $id = qr/([a-zA-Z-0-9_\->\.]*)/; 38my $prefix = qr/\s(?:\*?|\&?|\([a-z_]* \*\))\s*/; 39 40my $name; 41my $line; 42my @bad; 43 44die "Usage: $0 file.c [...]\n" unless @ARGV; 45 46while (my $file = shift @ARGV) 47{ 48 open my $fh, "<", $file or die "read $file failed: $!\n"; 49 while (<$fh>) 50 { 51 if( /mbedtls_malloc\(/ ) { 52 if( /$id\s*=.*mbedtls_malloc\(/ ) { 53 push @bad, "$file:$line:$name" if $name; 54 $name = $1; 55 $line = $.; 56 } else { 57 push @bad, "$file:$.:???" unless /return mbedtls_malloc/; 58 } 59 } elsif( $name && /(?:$inits)\($prefix\Q$name\E\b/ ) { 60 undef $name; 61 } elsif( $name && $. - $line > $limit ) { 62 push @bad, "$file:$line:$name"; 63 undef $name; 64 undef $line; 65 } 66 } 67 close $fh or die; 68} 69 70print "$_\n" for @bad; 71