使用sprintf时的分段错误

时间:2016-12-11 12:22:55

标签: c linux

我正在尝试编写一个简单的设备驱动程序和应用程序来访问驱动程序。

驱动程序写入系统调用如下所示

static ssize_t dev_write(struct file *filp, const char *buff, size_t len, loff_t *off)
{
short c=0;
short ind =0;
short count=0;
memset(msg,0,100);
readPos=0;
while(len>0)
{
 msg[count++]=buff[ind++];
 len--;
}

以及工作的应用程序如下所示

int main()
{
char buf[100];
char i = 0;
char tag=0;

int fp;
char *val[10];
char *rval[10];
unsigned int a =18440;
unsigned int d =1;
memset(buf, 0, 10);
tag=1;
fp = open("/dev/dd",O_RDWR);
if (fp<0)
{ 
  printf("file failed to open\n");
}  
sprintf(val[1],"%d%x%x",tag,a,d);
write(fp,val[1],strlen(val[1]));
sprintf(rval[1],"%d%x",tag,a);// statement that causes segmentation fault
return 0;
}

现在,如果我再添加一个sprintf函数,如上图所示,则会导致分段错误。我不知道为什么?如果我删除此行,代码工作正常。可能是什么问题呢?当我调用sprintf并写为子函数调用时会发生同样的问题?

1 个答案:

答案 0 :(得分:1)

考虑到问题以及代码中其他地方发现的重大问题,我认为你是一名初学C程序员,而且可能是初学程序员。因此,我强烈建议您暂时不要使用内核。编写和调试用户空间的内容要容易得多。

我还必须注意,在问题下面可以找到的评论对我来说是特别的,因为他们只是在忽略大局的情况下解决了极其微小的问题。

首先,我将处理用户空间部分,然后对内核代码进行评论。

int main()
{
char buf[100];

在你的实际代码中,函数的代码也从行的开头开始?通常情况下,人们会缩进至少4个空格,如果不是8个。或者是一个标签。

char i = 0;

未使用的变量。

char tag=0;

为什么这里设置为0,为什么风格与上面的线不同? (“=”vs“”)

int fp;

文件描述符通常被命名为“fd”。 'fp'用于从例如的fopen。

char *val[10];
char *rval[10];
unsigned int a =18440;
unsigned int d =1;

还有另一种方式。

memset(buf, 0, 10);

为什么?

tag=1;

这会覆盖声明中的作业。

fp = open("/dev/dd",O_RDWR);
if (fp<0)
{ 
  printf("file failed to open\n");

这应该打印收到的实际错误,例如与恐怖。另请注意,尽管出现错误,程序仍会继续执行。

}  
sprintf(val[1],"%d%x%x",tag,a,d);

正如其中一条评论中正确指出的那样,val [1]的值未指定,写入该值是未定义的行为。鉴于问题的严重性,我认为您需要重新阅读有关指针和字符串处理的课程。

write(fp,val[1],strlen(val[1]));
通过捕获sprintf的返回值

可以轻松避免使用strlen
sprintf(rval[1],"%d%x",tag,a);// statement that causes segmentation fault

与之前的sprintf相同的问题。

return 0;
}

现在是内核部分。虽然粘贴的样本不完整,但只看模块是错误的。

static ssize_t dev_write(struct file *filp, const char *buff, size_t len, loff_t *off)
{
short c=0;
short ind =0;
short count=0;

为什么这些类型很短?这是如下所述的问题。另外你为什么要把它们设置为0?

memset(msg,0,100);

为什么memset甚至被执行了?

不知道msg是什么,但重复的100强烈提示代码是错误的。如果这是静态缓冲区,则应使用sizeof(msg)。否则应该有一个宏或一个大小的变量。

readPos=0;

这很可能是错误的。

while(len>0)

len没有针对缓冲区大小进行验证,因此特别是您可以开始写入缓冲区。还要注意如何使用专用的方式表达缓冲区大小将避免再次重复数字100。

{
 msg[count++]=buff[ind++];

len的类型为size_t,它比short更长,用于count和ind。因此,如果尺寸大于可以用正号表示的尺寸,那么实际上这将从+ 32k翻转到-32k。也就是说,您将开始将之前的内容写入缓冲区。如果碰巧没有崩溃,你将重复提供足够大的len周期。

但这确实是一个小问题,因为对用户空间缓冲区的访问不正确。从别处引用自己: 以这种方式访问​​是一种安全性和可靠性威胁。考虑当用户传递内核中的某个地址或者是垃圾时会发生什么。此外,它会在几种情况下不起作用(例如SMAP或具有从用户空间分割的内核地址空间的体系结构)。

您需要get_user,copy_from_user或类似。

 len--;
}