I'm trying to write a simple socket filter eBPF program that can access the socket buffer data.
#include <linux/bpf.h>
#include <linux/if_ether.h>
#define SEC(NAME) __attribute__((section(NAME), used))
SEC("socket_filter")
int myprog(struct __sk_buff *skb) {
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
struct ethhdr *eth = data;
if ((void*)eth + sizeof(*eth) > data_end)
return 0;
return 1;
}
And I'm compiling using clang:
clang -I./ -I/usr/include/x86_64-linux-gnu/asm \
-I/usr/include/x86_64-linux-gnu/ -O2 -target bpf -c test.c -o test.elf
However when I try to load the program I get the following verifier error:
invalid bpf_context access off=80 size=4
My understanding of this error is that it should be thrown when you try to access context data that hasn't been checked to be within data_end, however my code does do that:
Here is the instructions for my program
0000000000000000 packet_counter:
0: 61 12 50 00 00 00 00 00 r2 = *(u32 *)(r1 + 80)
1: 61 11 4c 00 00 00 00 00 r1 = *(u32 *)(r1 + 76)
2: 07 01 00 00 0e 00 00 00 r1 += 14
3: b7 00 00 00 01 00 00 00 r0 = 1
4: 3d 12 01 00 00 00 00 00 if r2 >= r1 goto +1 <LBB0_2>
5: b7 00 00 00 00 00 00 00 r0 = 0
which would imply that the error is being caused by reading the pointer to data_end? However it only happens if I don't try to check the bounds later.
This is because your BPF program is a “socket filter”, and that such programs are not allowed to do direct packet access (see sk_filter_is_valid_access(), where we return false on trying to read skb->data or skb->data_end for example). I do not know the specific reason why it is not available, although I suspect this would be a security precaution as socket filter programs may be available to unprivileged users.
Your program loads just fine as a TC classifier, for example (bpftool prog load foo.o /sys/fs/bpf/foo type classifier -- By the way thanks for the standalone working reproducer, much appreciated!).
If you want to access data for a socket filter, you can still use the bpf_skb_load_bytes() (or bpf_skb_store_bytes()) helper, which automatically does the check on length. Something like this:
#include <linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))
static void *(*bpf_skb_load_bytes)(const struct __sk_buff *, __u32,
void *, __u32) =
(void *) BPF_FUNC_skb_load_bytes;
SEC("socket_filter")
int myprog(struct __sk_buff *skb)
{
__u32 foo;
if (bpf_skb_load_bytes(skb, 0, &foo, sizeof(foo)))
return 0;
if (foo == 3)
return 0;
return 1;
}
Regarding your last comment:
However it only happens if I don't try to check the bounds later.
I suspect clang compiles out the assignments for data and data_end if you do not use them in your code, so they are no longer present and no longer a problem for the verifier.
Related
I am trying to build quick nodejs script to look at some data in my network. Using node_pcap I manage to decode almost everything but the payload data that is end through the UDP protocol. This is my code (fairly basic but gives me the headers and payloads)
const interface = 'en0';
let filter = 'udp';
const pcap = require('pcap'),
pcap_session = pcap.createSession(interface, filter),
pcap_session.on('packet', function (raw_packet) {
let packet = pcap.decode.packet(raw_packet);
let data = packet.payload.payload.payload.data;
console.log(data.toString()); // not full data
});
When I try to print data using toString() method, it gives me most of the data but the beginning. I have something like this printed :
Li?��ddn-�*ys�{"Id":13350715,... I've cut the rest of the data which is the rest of the JSON.
But I am suspecting that the bit of data that I can't read contain some useful info such has how many packet, offset packet and so on..
I manage to get a part of it from the buffer from a payload :
00 00 00 01 52 8f 0b 4a 4d 3f cb de 08 00 01 00 00 00 04 a4 00 00 26 02 00 00 26 02 00 00 00 03 00 00 00 00 00 00 09 2d 00 00 00 00 f3 03 01 00 00 2a 00 02 00 79 00 05 73 01 d2
Although I have an idea of what kind of data it can be I have no idea of its structure.
Is there a way that I could decode this bit of the buffer ? I tried to look at some of the buffer method such as readInt32LE, readInt16LE but in vain. Is there some reading somewhere that can guide me through the process of decoding it?
[Edit] The more I looked into it, the more I suspect the data to be BSON and not JSON, that would explain why I can read some bit of it but not everything. Any chance someone manage to decode BSON from a packet ?
How does the library know which packet decoder to use?
It starts at Layer 2 of the TCP/IP stack by identifying which protocol is used https://github.com/node-pcap/node_pcap/blob/master/decode/pcap_packet.js#L29-L56
switch (this.link_type) {
case "LINKTYPE_ETHERNET":
this.payload = new EthernetPacket(this.emitter).decode(buf, 0);
break;
case "LINKTYPE_NULL":
this.payload = new NullPacket(this.emitter).decode(buf, 0);
break;
case "LINKTYPE_RAW":
this.payload = new Ipv4(this.emitter).decode(buf, 0);
break;
case "LINKTYPE_IEEE802_11_RADIO":
this.payload = new RadioPacket(this.emitter).decode(buf, 0);
break;
case "LINKTYPE_LINUX_SLL":
this.payload = new SLLPacket(this.emitter).decode(buf, 0);
break;
default:
console.log("node_pcap: PcapPacket.decode - Don't yet know how to decode link type " + this.link_type);
}
Then it goes upper and tries to decode the proper protocol based on the flags it finds in the header https://github.com/node-pcap/node_pcap/blob/master/decode/ipv4.js#L12-L17 in this particular case for the IPv4 protocol
IPFlags.prototype.decode = function (raw_flags) {
this.reserved = Boolean((raw_flags & 0x80) >> 7);
this.doNotFragment = Boolean((raw_flags & 0x40) > 0);
this.moreFragments = Boolean((raw_flags & 0x20) > 0);
return this;
};
Then in your case it would match with the udp protocol https://github.com/node-pcap/node_pcap/blob/master/decode/ip_protocols.js#L15
protocols[17] = require("./udp");
Hence, If you check https://github.com/node-pcap/node_pcap/blob/master/decode/udp.js#L32 the packet is automatically decoded and it exposes a toString method
UDP.prototype.toString = function () {
var ret = "UDP " + this.sport + "->" + this.dport + " len " + this.length;
if (this.sport === 53 || this.dport === 53) {
ret += (new DNS().decode(this.data, 0, this.data.length).toString());
}
return ret;
};
What does this mean for you?
In order to decode a udp(any) packet you just call the high level api pcap.decode.packet(raw_packet) and then call toString method to display the decoded body payload
pcap_session.on('packet', function (raw_packet) {
let packet = pcap.decode.packet(raw_packet);
console.log(packet.toString());
});
Below, you see a simple applet that returns 0x6781 to incoming APDU commands with INS=0x70 or INS=0x71:
package testPack;
import javacard.framework.*;
public class TestApp extends Applet
{
public static void install(byte[] bArray, short bOffset, byte bLength)
{
new TestApp().register(bArray, (short) (bOffset + 1), bArray[bOffset]);
}
public void process(APDU apdu)
{
if (selectingApplet())
{
return;
}
byte[] buf = apdu.getBuffer();
switch (buf[ISO7816.OFFSET_INS])
{
case (byte)0x70:
ISOException.throwIt((short)0x6781);
break;
case (byte)0x71:
ISOException.throwIt((short)0x6781);
break;
default:
ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
}
}
}
The problem is that, I receive 0x6C01 to the APDU command with INS=0x70:
Send: 00 A4 04 00 07 01 02 03 04 05 00 00 00
Recv: 90 00
Send: 00 70 00 00 00
Recv: 6C 01
Send: 00 70 00 00 01
Recv: 01 90 00
Send: 00 71 00 00 00
Recv: 67 81
I tried two different Java Cards (One is NXP JCOP v2.4.2 r3 and another is a KONA java card) through both contact and contactless interfaces and using two different cap file generated inside two different laptops via two different IDE!!!( How Suspicious am I? :D ) But the response is equal.
I suspect to the PCSC or Card Manager for this weird response. Because in the simulator, even the process method doesn't called for this special INS value.
What's wrong with it?
INS = 70with CLA = 00 is the MANAGE CHANNEL command according to the ISO-7816 specification, as well as INS = A4 means SELECT.
If you want to use these INS codes, you must use CLA >= 0x80 in order to specify that it is your proprietary command.
I think if class represent interindustry class then only INS will work as defined in standard,Here CLA - 00 represent interindusty command therefore, card response behvaiour was like same behaviour as Manage channel command because you used INS = 70.
6.16.4 Response message (nominal case)
Table 73 - MANAGE CHANNEL response APDU
Data field Logical channel number if P1-P2='0000'
Empty if P1-P2!='0000'
SW1-SW2 Status bytes
actually your card was returning logical channel no -01 to you. ManageChannel
it seems to me that if class is propriatry. here INS will not treated as defined INS in standard. Hope CLA 80 with same INS 0x70 will give you require result.
hope it helps.
[Bit 8 set to 1 indicates the proprietary class]
I am getting a strange crash. I am writing data on bluetooth device. when the loop ends the app gets SIGABRT. I am not able to figure out the crash till now. Could someone help me to fix this ?
The app crashes in this function if the loop is called 2 times and it does not crash if it called 1 time.
BOOL success = [strongSelf.CronoDeviceObject.deviceHandler writeSettingsScreenData:data module:strongSelf.CronoDeviceObject.moduleNumber];
#pragma mark Button Save and cancel
- (IBAction)OkTaped:(id)sender{
//for submit
if ([self validateData]) {
[[ProgressHUD defaultHUD] setLoadingText:CVLocalizedString(#"Updating",nil)];
[[ProgressHUD defaultHUD]showInView:self.view];
[self performSelector:#selector(saveAllSettings) withObject:nil afterDelay:1.0];
}
}
- (void)saveAllSettings {
__weak __typeof__(self) weakSelf = self;
dispatch_queue_t queue = dispatch_queue_create("writesettings",NULL);
dispatch_async(queue, ^{
__strong __typeof__(self) strongSelf = weakSelf;
if (strongSelf.CronoDeviceObject) {
[strongSelf.CronoDeviceObject.deviceHandler writeFastWindUp:strongSelf.CronoDeviceObject.moduleNumber];
[strongSelf.CronoDeviceObject.deviceHandler writeLightOnOff:strongSelf.CronoDeviceObject.moduleNumber state:strongSelf.btnCheckBox2.selected?0x01:0x00];
strongSelf.CronoDeviceObject.lightState = (strongSelf.btnCheckBox2.selected)?0:1;
NSLog(#"Button Light is %#",([strongSelf.CronoDeviceObject isEnergySavingMode])?#"ON":#"Off");
NSString* semicolon =[NSString stringWithFormat:#";%#;%#;%#",strongSelf.BrandNametxtfld.text,strongSelf.ModalNametxtfld.text,[NSString stringWithFormat:#"%#;", strongSelf.WatchWinderNametxtfld.text]];
NSMutableData* data = [[NSMutableData alloc] initWithData:[semicolon dataUsingEncoding:NSUTF8StringEncoding]];
if ([strongSelf.CronoDeviceObject.deviceHandler respondsToSelector:#selector(writeSettingsScreenData:module:)]) {
BOOL success = [strongSelf.CronoDeviceObject.deviceHandler writeSettingsScreenData:data module:strongSelf.CronoDeviceObject.moduleNumber];
**//The app does not get here if the above function loop called 2 times but if it called 1 time it's working fine.**
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
__strong __typeof__(self) strongSelf = weakSelf;
self.CronoDeviceObject.Devicemodal.Brandname = strongSelf.BrandNametxtfld.text;
self.CronoDeviceObject.Devicemodal.modalName = strongSelf.ModalNametxtfld.text;
self.CronoDeviceObject.Devicemodal.winderName = strongSelf.WatchWinderNametxtfld.text;
// Update the UI
[[ProgressHUD defaultHUD]hideActivityIndicator];
[strongSelf.navigationController popViewControllerAnimated:YES];
});
}
}
}
});
}
- (BOOL) writeSettingsScreenData:(NSMutableData*) payload module:(Byte) module {
if (payload == nil) {
return false;
}
int numwrites = 1;
numwrites += (payload.length - 1)/32;
numwrites = (numwrites>8)?8:numwrites;
for(int i = 0; i < numwrites; i++){
//#try {
NSUInteger bufferLength = (payload.length - i*32);
Byte buff[32];
memset(buff, 0, 32);
[payload getBytes:&buff range:NSMakeRange(i*32, bufferLength)];
self.finalString = #"";
//rcvbuff = new StringBuffer(32);
self.chronoVisionMessage = [ChronoVisionMessage new];
//Set Line Address And Dir(R)
// chronoVisionMessage.setLineAddr(ChronoVisionMessage.LINE_BROADCAST);
// chronoVisionMessage.setLineDir(ChronoVisionMessage.LINE_WRITE);
//Set Module (communicnMstr/Address)
[self.chronoVisionMessage setModRsvd:(MODULE_BUSMASTER)];//set to 0
[self.chronoVisionMessage setModCh:(MODULE_BUSMASTER)];//set to default zero
if (module == 0) {
[self.chronoVisionMessage setModAddr:(MODULE_BROADCAST)];
}
else{
[self.chronoVisionMessage setModAddr:module];
}
//Set Register Access(R)/Addresss
[self.chronoVisionMessage setRegRw:REIGSTER_WRITE];
[self.chronoVisionMessage setRegType:REIGSTER_ACCESSTYPE1];//Access standard reg
[self.chronoVisionMessage setRegAddr:REIGSTER_ACCESSTYPE0];//set to 0
//[self.chronoVisionMessage setValueHi:REIGSTER_ACCESSTYPE0];//set to 0
[self.chronoVisionMessage setValueLo:((Byte)i)];//set to 0 to 7
[self.chronoVisionMessage setNewData:[NSMutableData dataWithBytes:buff length:32]];//set to 0
#try {
NSMutableData* header = [self.chronoVisionMessage getMessage];
NSMutableData* final = [[NSMutableData alloc] initWithBytes:[header bytes] length:[header length]];
[final appendBytes:buff length:32];
[self writeData:final];
int retryCount = 0;
while (!self.allDataReceived && retryCount<kTimeout) {
[NSThread sleepForTimeInterval:0.1];
}
//String allData = read(5000);
if(self.finalString.length == 0){
//_peripheral.setListener(null);
return false;
}
if ([self.finalString length] >= 8 && [self.finalString rangeOfString:#"\r"].location > 0) {
self.finalString = [self.finalString substringWithRange:NSMakeRange(0, [self.finalString rangeOfString:#"\r"].location)];
}
NSData *finalData = [self.finalString dataFromHex];
NSLog(#"final readBuff is %#",finalData);
if (i==(numwrites-1) && finalData.length >= 36) {
return true;
}
//free(buff);
}
#catch (NSException *exception) {
NSLog(#"Exception occured when writing user data %#",[exception description]);
}
}
return false;
}
here is an image after the crash
here is the back trace log
bt
* thread #12: tid = 0x13cf99, 0x3611c9aa libsystem_c.dylib`__abort + 102, queue = 'com.apple.root.default-qos', stop reason = EXC_BREAKPOINT (code=EXC_ARM_BREAKPOINT, subcode=0xdefe)
* frame #0: 0x3611c9aa libsystem_c.dylib`__abort + 102
frame #1: 0x3611cf94 libsystem_c.dylib`__stack_chk_fail + 180
frame #2: 0x0007a2d4 Chronovision`-[DeviceHandler writeSettingsScreenData:module:](self=0x17081990, _cmd=0x001069c2, payload=0x00000000, module='\x02') + 3028 at DeviceHandler.m:435
frame #3: 0x000d1f2c Chronovision`__34-[CVDetailViewController OkTaped:]_block_invoke(.block_descriptor=<unavailable>) + 400 at CVDetailViewController.m:357
frame #4: 0x002b219a libdispatch.dylib`_dispatch_call_block_and_release + 10
frame #5: 0x002bcc48 libdispatch.dylib`_dispatch_root_queue_drain + 1596
frame #6: 0x002bde20 libdispatch.dylib`_dispatch_worker_thread3 + 108
frame #7: 0x361fada8 libsystem_pthread.dylib`_pthread_wqthread + 668
(lldb)
Update
I ran the app in xcode 7.1.1 and I got the following log in console when the app crashes.
Dec 7 16:55:09 Rahuls-iPad Chronovision[1650] <Error>: ================================================================
==1650==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x088e06a0 at pc 0x0036f4a9 bp 0x088e02c4 sp 0x088dfe94
WRITE of size 42 at 0x088e06a0 thread T23
#0 0x36f4a7 in wrap_memmove (/private/var/mobile/Containers/Bundle/Application/33337E25-C69D-49F5-ABA1-5AD65673725C/Chronovision.app/Frameworks/libclang_rt.asan_ios_dynamic.dylib+0x2a4a7)
#1 0x287936b3 in <redacted> (/System/Library/Frameworks/Foundation.framework/Foundation+0x236b3)
#2 0x8a971 in -[DeviceHandler writeSettingsScreenData:module:] (/private/var/mobile/Containers/Bundle/Application/33337E25-C69D-49F5-ABA1-5AD65673725C/Chronovision.app/Chronovision+0x2d971)
#3 0x139eaf in __41-[CVDetailViewController saveAllSettings]_block_invoke (/private/var/mobile/Containers/Bundle/Application/33337E25-C69D-49F5-ABA1-5AD65673725C/Chronovision.app/Chronovision+0xdceaf)
#4 0x374e15 in __wrap_dispatch_async_block_invoke (/private/var/mobile/Containers/Bundle/Application/33337E25-C69D-49F5-ABA1-5AD65673725C/Chronovision.app/Frameworks/libclang_rt.asan_ios_dynamic.dylib+0x2fe15)
#5 0x853199 in _dispatch_call_block_and_release (/usr/lib/system/introspection/libdispatch.dylib+0x1199)
#6 0x85bd87 in _dispatch_queue_drain (/usr/lib/system/introspection/libdispatch.dylib+0x9d87)
#7 0x855ac3 in _dispatch_queue_invoke (/usr/lib/system/introspection/libdispatch.dylib+0x3ac3)
#8 0x85db19 in _dispatch_root_queue_drain (/usr/lib/system/introspection/libdispatch.dylib+0xbb19)
#9 0x85ee1f in _dispatch_worker_thread3 (/usr/lib/system/introspection/libdispatch.dylib+0xce1f)
#10 0x361fada7 in _pthread_wqthread (/usr/lib/system/libsystem_pthread.dylib+0xda7)
#11 0x361faafb in start_wqthread (/usr/lib/system/libsystem_pthread.dylib+0xafb)
Address 0x088e06a0 is located in stack of thread T23 at offset 128 in frame
#0 0x8a427 in -[DeviceHandler writeSettingsScreenData:module:] (/private/var/mobile/Containers/Bundle/Application/33337E25-C69D-49F5-ABA1-5AD65673725C/Chronovision.app/Chronovision+0x2d427)
This frame has 12 object(s):
[16, 24) 'r.i1'
[48, 56) 'r.i'
[80, 84) ''
[96, 128) 'buff'
[160, 168) '' <== Memory access at offset 128 partially underflows this variable
[192, 196) 'header'
[208, 212) 'final'
[224, 232) ''
[256, 264) ''
[288, 296) ''
[320, 324) 'finalData'
[336, 340) 'exception'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
Thread T23 created by T0 here:
<empty stack>
SUMMARY: AddressSanitizer: stack-buffer-overflow ??:0 wrap_memmove
Shadow bytes around the buggy address:
0x4111c080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x4111c090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x4111c0a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x4111c0b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x4111c0c0: 00 00 00 00 f1 f1 00 f2 f2 f2 00 f2 f2 f2 04 f2
=>0x4111c0d0: 00 00 00 00[f2]f2 f2 f2 00 f2 f2 f2 04 f2 04 f2
0x4111c0e0: 00 f2 f2 f2 00 f2 f2 f2 00 f2 f2 f2 04 f2 04 f3
0x4111c0f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x4111c100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x4111c110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x4111c120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==1650==ABORTING
I got the problem with xcode 7.1.1.
I don't know why xcode 6.2 was unable to detect the crash location.
The problem was in the range I was using in the function
- (BOOL) writeSettingsScreenData:(NSMutableData*) payload module:(Byte) module {
if (payload == nil) {
return false;
}
int numwrites = 1;
numwrites += (payload.length - 1)/32;
numwrites = (numwrites>8)?8:numwrites;
for(int i = 0; i < numwrites; i++){
//#try {
//Changed this
NSUInteger bufferLength = (payload.length - i*32);
Byte buff[32];
memset(buff, 0, 32);
[payload getBytes:&buff range:NSMakeRange(i*32, bufferLength)];
//To this
NSUInteger bufferLength = ((payload.length - i*32)>32)?32:(payload.length-i*32);
Byte buff[32];
memset(buff, 0, 32);
if (payload && payload.length > bufferLength) {
[payload getBytes:&buff range:NSMakeRange(i*32, bufferLength)];
}
self.finalString = #"";
//rcvbuff = new StringBuffer(32);
self.chronoVisionMessage = [ChronoVisionMessage new];
//Set Line Address And Dir(R)
// chronoVisionMessage.setLineAddr(ChronoVisionMessage.LINE_BROADCAST);
// chronoVisionMessage.setLineDir(ChronoVisionMessage.LINE_WRITE);
//Set Module (communicnMstr/Address)
[self.chronoVisionMessage setModRsvd:(MODULE_BUSMASTER)];//set to 0
[self.chronoVisionMessage setModCh:(MODULE_BUSMASTER)];//set to default zero
if (module == 0) {
[self.chronoVisionMessage setModAddr:(MODULE_BROADCAST)];
}
else{
[self.chronoVisionMessage setModAddr:module];
}
//Set Register Access(R)/Addresss
[self.chronoVisionMessage setRegRw:REIGSTER_WRITE];
[self.chronoVisionMessage setRegType:REIGSTER_ACCESSTYPE1];//Access standard reg
[self.chronoVisionMessage setRegAddr:REIGSTER_ACCESSTYPE0];//set to 0
//[self.chronoVisionMessage setValueHi:REIGSTER_ACCESSTYPE0];//set to 0
[self.chronoVisionMessage setValueLo:((Byte)i)];//set to 0 to 7
[self.chronoVisionMessage setNewData:[NSMutableData dataWithBytes:buff length:32]];//set to 0
#try {
NSMutableData* header = [self.chronoVisionMessage getMessage];
NSMutableData* final = [[NSMutableData alloc] initWithBytes:[header bytes] length:[header length]];
[final appendBytes:buff length:32];
[self writeData:final];
int retryCount = 0;
while (!self.allDataReceived && retryCount<kTimeout) {
[NSThread sleepForTimeInterval:0.1];
}
//String allData = read(5000);
if(self.finalString.length == 0){
//_peripheral.setListener(null);
return false;
}
if ([self.finalString length] >= 8 && [self.finalString rangeOfString:#"\r"].location > 0) {
self.finalString = [self.finalString substringWithRange:NSMakeRange(0, [self.finalString rangeOfString:#"\r"].location)];
}
NSData *finalData = [self.finalString dataFromHex];
NSLog(#"final readBuff is %#",finalData);
if (i==(numwrites-1) && finalData.length >= 36) {
return true;
}
//free(buff);
}
#catch (NSException *exception) {
NSLog(#"Exception occured when writing user data %#",[exception description]);
}
}
return false;
}
I have read this link Analyzing kmemleak result, but it seems not give the final answer.
After scanning the memory leak by kmemleak. I can derive the result of unreferenced object's address, say 0xffff880060bb3060 [in the update].
and I use the following gdb command to see the location of this address.
Note I found the .text_address in /sys/module/hello/session/.text, and the value is 0xffffffffa038c000
sudo insmod hello.ko
gdb hello.ko /proc/kcore
add-symbol-file hello.ko 0xffffffffa038c000
list *0xffff880060bb3060
However the list command show nothing.
I have used these steps to debug of kernel oops message, so I'm sure the command I type is correct.
So how can I use gdb to analyze the result given by kmemleak?
Update
I think I should also provide my sample code hello.c:
#include <linux/module.h>
#include <linux/list.h>
#include <linux/slab.h>
struct num {
struct list_head node;
int number;
};
static int __init hello_init(void)
{
LIST_HEAD(my_list);
struct num *tmp;
struct list_head *iterator;
u8 i;
for(i=0; i<5; i++) {
tmp = kmalloc(sizeof(struct num), GFP_KERNEL); //I expect this line will be output as memory leak
tmp->number = i;
list_add(&tmp->node, &my_list);
}
list_for_each(iterator, &my_list) {
printk("%d\n", list_entry(iterator, struct num, node)->number);
}
return 0;
}
static void hello_exit(void)
{
printk(KERN_ALERT "Good, haha\n");
}
module_init(hello_init);
module_exit(hello_exit);
And the following is the scan result:
unreferenced object 0xffff880060bb3060 (size 32):
comm "swapper/0", pid 1, jiffies 4294895149 (age 802.568s)
hex dump (first 32 bytes):
00 01 10 00 00 00 ad de 00 02 20 00 00 00 ad de .......... .....
00 28 b1 57 00 88 ff ff 01 4f 0a 56 00 00 00 00 .(.W.....O.V....
backtrace:
[<ffffffff8170ea7e>] kmemleak_alloc+0x4e/0xc0
[<ffffffff811a5de8>] kmem_cache_alloc_trace+0xb8/0x160
[<ffffffff810b0ed6>] pm_vt_switch_required+0x76/0xb0
[<ffffffff813c1e61>] register_framebuffer+0x1d1/0x370
[<ffffffff813db3fc>] vesafb_probe+0x5bc/0xa70
[<ffffffff814870cc>] platform_drv_probe+0x3c/0x70
[<ffffffff81484d74>] driver_probe_device+0x94/0x3d0
[<ffffffff8148517b>] __driver_attach+0x8b/0x90
[<ffffffff81482913>] bus_for_each_dev+0x73/0xb0
[<ffffffff814847ae>] driver_attach+0x1e/0x20
[<ffffffff814842f8>] bus_add_driver+0x208/0x2e0
[<ffffffff81485844>] driver_register+0x64/0xf0
[<ffffffff81486eca>] __platform_driver_register+0x4a/0x50
[<ffffffff81d923f1>] vesafb_driver_init+0x12/0x14
[<ffffffff810020f2>] do_one_initcall+0xf2/0x1b0
[<ffffffff81d50f24>] kernel_init_freeable+0x144/0x1cc
I was trying to write a pci driver which can display the MAC address of my Ethernet card.
Running a Ubuntu on VM and my Ethernet card is Intel one as follows
00:08.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 02)
I was able to get the data sheet of the same from Intel website and as per data sheet it says IO address are mapped to Bar 2 (Refer to pg 87) and MAC can be read using RAL/RAH register which are at offset RAL (05400h + 8*n; R/W) and RAH (05404h + 8n; R/W)
2 18h IO Register Base Address (bits 31:2) 0b mem
Based on this information, i wrote a small PCI driver but i always get the MAC as fff and when i debugged further, i see io_base address is always zero.
Below is the code
1 /*
2 Program to find a device on the PCI sub-system
3 */
4 #define VENDOR_ID 0x8086
5 #define DEVICE_ID 0x100e
6
7 #include <linux/kernel.h>
8 #include <linux/module.h>
9 #include <linux/stddef.h>
10 #include <linux/pci.h>
11 #include <linux/init.h>
12 #include <linux/cdev.h>
13 #include <linux/device.h>
14 #include <asm/io.h>
15
16 #define LOG(string...) printk(KERN_INFO string)
17
18 #define CDEV_MAJOR 227
19 #define CDEV_MINOR 0
20
21
22 MODULE_LICENSE("GPL");
23
24 struct pci_dev *pci_dev;
25 unsigned long mmio_addr;
26 unsigned long reg_len;
27 unsigned long *base_addr;
28
29 int device_probe(struct pci_dev *dev, const struct pci_device_id *id);
30 void device_remove(struct pci_dev *dev);
31
32 struct pci_device_id pci_device_id_DevicePCI[] =
33 {
34 {VENDOR_ID, DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
35 };
36
37 struct pci_driver pci_driver_DevicePCI =
38 {
39 name: "MyPCIDevice",
40 id_table: pci_device_id_DevicePCI,
41 probe: device_probe,
42 remove: device_remove
43 };
44
45
46 int init_module(void)
47 {
48 //struct pci_dev *pdev = NULL;
49 int ret = 0;
50
51 pci_register_driver(&pci_driver_DevicePCI);
52
53 return ret;
54 }
55
56 void cleanup_module(void)
57 {
58 pci_unregister_driver(&pci_driver_DevicePCI);
59
60 }
61
62 #define REGISTER_OFFSET 0x05400
64 int device_probe(struct pci_dev *dev, const struct pci_device_id *id)
65 {
66 int ret;
67 int bar = 2; // Bar to be reserved
68 unsigned long io_base = 0;
69 unsigned long mem_len = 0;
70 unsigned int register_data = 0;
71
72 LOG("Device probed");
73
74 /* Reserve the access to PCI device */
75 ret = pci_request_region(dev, bar, "my_pci");
76 if (ret) {
77 printk(KERN_ERR "request region failed :%d\n", ret);
78 return ret;
79 }
80
81 ret = pci_enable_device(dev);
82 if (ret < 0 ) LOG("Failed while enabling ... ");
83
84 io_base = pci_resource_start(dev, bar);
85 mem_len = pci_resource_len(dev, bar);
86
87 request_region(io_base, mem_len, "my_pci");
88 register_data = inw(io_base + REGISTER_OFFSET);
89 printk(KERN_INFO "IO base = %lx", io_base);
90 printk(KERN_INFO "MAC = %x", register_data);
91
92 return ret;
93 }
94
95 void device_remove(struct pci_dev *dev)
96 {
97 pci_release_regions(dev);
98 pci_disable_device(dev);
99 }
100
lspci -x output of my card
00:08.0 Ethernet controller: Intel Corporation 82540EM Gigabit Ethernet Controller (rev 02)
00: 86 80 0e 10 07 00 30 02 02 00 00 02 00 40 00 00
10: 00 00 82 f0 00 00 00 00 41 d2 00 00 00 00 00 00
20: 00 00 00 00 00 00 00 00 00 00 00 00 86 80 1e 00
30: 00 00 00 00 dc 00 00 00 00 00 00 00 09 01 ff 00
Can any one let me know what am i doing wrong?
I've modified your code and commented on changes. I have removed all of your existing comments to avoid confusion, and have only modified your probe function.
/* We need a place to store a logical address for unmapping later */
static void* logical_address;
int device_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int ret;
int bar_mask; /* BAR mask (this variable) and the integer BAR */
int requested_bar = 2; /* (this variable) are not the same thing, so give them */
/* separate variables */
resource_size_t io_base = 0; /* use kernel macros instead of built-in datatypes */
resource_size_t mem_len = 0;
unsigned int register_data = 0;
LOG("Device probed");
/* add this call to get the correct BAR mask */
bar_mask = pci_select_bars(dev, 0);
/* switched order - enable device before requesting memory */
ret = pci_enable_device(dev);
if (ret < 0 ) LOG("Failed while enabling ... ");
/* for this call, we want to pass the BAR mask, NOT the integer bar we want */
ret = pci_request_region(dev, bar_mask, "my_pci");
if (ret) {
printk(KERN_ERR "request region failed :%d\n", ret);
return ret;
}
/* it is in THESE calls that we request a specific BAR */
io_base = pci_resource_start(dev, requested_bar);
mem_len = pci_resource_len(dev, requested_bar);
/* you don't need to request anything again, so get rid of this line: */
/* request_region(io_base, mem_len, "my_pci"); */
/* you're missing an important step: we need to translate the IO address
* to a kernel logical address that we can actually use. Add a call to
* ioremap()
*/
logical_address = ioremap(io_base, mem_len);
/* we need to use the logical address returned by ioremap(), not the physical
* address returned by resource_start
*/
register_data = inw(logical_address + REGISTER_OFFSET);
printk(KERN_INFO "IO base = %lx", io_base);
printk(KERN_INFO "MAC = %x", register_data);
return ret;
}
You will need to add a corresponding call to iounmap() in your device_remove() routine. Take a look at the Intel E100E driver source code for some good examples.