Bug 214711 - Information leak from kernel to user space in scsi_ioctl.c
Summary: Information leak from kernel to user space in scsi_ioctl.c
Status: NEW
Alias: None
Product: SCSI Drivers
Classification: Unclassified
Component: Other (show other bugs)
Hardware: All Linux
: P1 normal
Assignee: scsi_drivers-other
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2021-10-13 20:58 UTC by Andrew Bao
Modified: 2021-10-16 21:30 UTC (History)
1 user (show)

See Also:
Kernel Version: 5.15-rc5
Subsystem:
Regression: No
Bisected commit-id:


Attachments

Description Andrew Bao 2021-10-13 20:58:40 UTC
Hi Maintainer,
I just found an uninitialized value use bug that causes memory leakage from kernel to user space. Here are the details:

Vulnerable function is in /drivers/scsi/scsi_ioctl.c



static int scsi_put_cdrom_generic_arg(const struct cdrom_generic_command *cgc,
				      void __user *arg)
{
#ifdef CONFIG_COMPAT
	if (in_compat_syscall()) {
		struct compat_cdrom_generic_command cgc32 = {
			.buffer		= (uintptr_t)(cgc->buffer),
			.buflen		= cgc->buflen,
			.stat		= cgc->stat,
			.sense		= (uintptr_t)(cgc->sense),
			.data_direction	= cgc->data_direction,
			.quiet		= cgc->quiet,
			.timeout	= cgc->timeout,
			.unused		= (uintptr_t)(cgc->unused),
		};
		memcpy(&cgc32.cmd, &cgc->cmd, CDROM_PACKET_SIZE);

		if (copy_to_user(arg, &cgc32, sizeof(cgc32))) 
			return -EFAULT;

		return 0;
	}
#endif
	if (copy_to_user(arg, cgc, sizeof(*cgc)))
		return -EFAULT;

	return 0;
}

The issue is, struct cgc32 is partially initialized since pad[3] are not initialized. Then this struct is passed to copy_to_user, and 3 bytes are leaked from kernel space to userspace. 


The struct is declared here:
struct compat_cdrom_generic_command {
	unsigned char	cmd[CDROM_PACKET_SIZE];
	compat_caddr_t	buffer;
	compat_uint_t	buflen;
	compat_int_t	stat;
	compat_caddr_t	sense;
	unsigned char	data_direction;
	unsigned char	pad[3];
	compat_int_t	quiet;
	compat_int_t	timeout;
	compat_caddr_t	unused;
};
#endif
Comment 1 Bart Van Assche 2021-10-14 04:06:05 UTC
Isn't this called an information leak instead of a memory leak?

Additionally, my understanding of the C standard is that a compiler is required to zero-initialize members that have not been mentioned in an initializer list. From the ANSI C 202x draft: "The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject; all subobjects that are not
initialized explicitly shall be initialized implicitly the same as objects that have static storage duration."
Comment 2 Andrew Bao 2021-10-14 15:15:22 UTC
Hi Bart,
Yes, It is an information leak.

"my understanding of the C standard is that a compiler is required to zero-initialize members that have not been mentioned in an initializer list. From the ANSI C 202x d"raft: "The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject; all subobjects that are not
initialized explicitly shall be initialized implicitly the same as objects that have static storage duration."

I am wondering in what condition the compiler will zero-initialize the field in a struct. And what is the initializer in the context? Let say we have a struct foo:

struct foo{
       int  a;
       int b;
       int c;
};
method 1:

struct foo f; 
f.a = 1;
f.b = 2;

In method 1, will the compiler zero-initialize the field f.c?

method 2:
struct foo f = {
			.a	=   1
			.b      =   2
		
		};
In method 2, will the compiler zero-initialize the field f.c?


By the way,
struct compat_cdrom_generic_command {
	unsigned char	cmd[CDROM_PACKET_SIZE];
	compat_caddr_t	buffer;
	compat_uint_t	buflen;
	compat_int_t	stat;
	compat_caddr_t	sense;
	unsigned char	data_direction;
	unsigned char	pad[3];
	compat_int_t	quiet;
	compat_int_t	timeout;
	compat_caddr_t	unused;
};
If this struct does not declare unsigned char pad[3] in order to fill with padding, will the compiler zero-initialize 3 bytes holes for this struct?
Comment 3 Andrew Bao 2021-10-14 15:16:28 UTC
Hi Bart,
Yes, It is an information leak.

"my understanding of the C standard is that a compiler is required to zero-initialize members that have not been mentioned in an initializer list. From the ANSI C 202x d"raft: "The initialization shall occur in initializer list order, each initializer provided for a particular subobject overriding any previously listed initializer for the same subobject; all subobjects that are not
initialized explicitly shall be initialized implicitly the same as objects that have static storage duration."

I am wondering in what condition the compiler will zero-initialize the field in a struct. And what is the initializer in the context? Let say we have a struct foo:

struct foo{
       int  a;
       int b;
       int c;
};
method 1:

struct foo f; 
f.a = 1;
f.b = 2;

In method 1, will the compiler zero-initialize the field f.c?

method 2:
struct foo f = {
			.a	=   1
			.b      =   2
		
		};
In method 2, will the compiler zero-initialize the field f.c?


By the way,
struct compat_cdrom_generic_command {
	unsigned char	cmd[CDROM_PACKET_SIZE];
	compat_caddr_t	buffer;
	compat_uint_t	buflen;
	compat_int_t	stat;
	compat_caddr_t	sense;
	unsigned char	data_direction;
	unsigned char	pad[3];
	compat_int_t	quiet;
	compat_int_t	timeout;
	compat_caddr_t	unused;
};
If this struct does not declare unsigned char pad[3] in order to fill with padding, will the compiler zero-initialize 3 bytes holes for this struct?
Comment 4 Bart Van Assche 2021-10-14 18:21:51 UTC
C and C++ compilers always initialize all named data members of a data structure in case of aggregate initialization. See also https://stackoverflow.com/questions/10828294/c-and-c-partial-initialization-of-automatic-structure. However, whether or not unnamed padding bytes and bits are initialized depends on the language standard supported by the compiler. See e.g. https://gustedt.wordpress.com/2012/10/24/c11-defects-initialization-of-padding/
Comment 5 Andrew Bao 2021-10-16 21:30:38 UTC
Thank you

Note You need to log in before you can comment on or make changes to this bug.