Bugzilla – Bug 65318
VUL-0: CVE-2005-0532: kernel: sign handling issues in 2.6
Last modified: 2021-11-02 16:35:27 UTC
Hello, we got this via vendor-sec. From: Marcelo Tosatti <marcelo.tosatti@cyclades.com> To: vendor-sec@lst.de Cc: torvalds@osdl.org, Alan Cox <alan@lxorguk.ukuu.org.uk>, Chris Wright <chrisw@osdl.org>, viro@parcelfarce.linux.theplanet.co.uk User-Agent: Mutt/1.5.5.1i Subject: [vendor-sec] Sign handling issues on v2.6 Errors-To: vendor-sec-admin@lst.de Date: Sun, 30 Jan 2005 12:30:42 -0200 Hi folks,rc.theaimsgroup.com/?l=linux-netdev&m=110608318200957&w=2 Georgi Guninski has discovered three important sign comparison problems in v2.6 kernels. - Ð ÐœÐµÐŒÐ°Ð»ÐŸÐ²Ð°Ð¶ÐœÐ°Ñ ÐŸÑПбеММПÑÑÑ - ПÑлОÑМÑй ЎОзайМ. The collection of vulnerabilities has led Linus to add appropriate range checking at higher VFS levels (ie sys_read{v}/sys_write{v}): ChangeSet@1.1966.1.68, 2005-01-25 20:29:47-08:00, torvalds@ppc970.osdl.org Add 'f_maxcount' to allow filesystems to set a per-file maximum IO size. ChangeSet@1.1966.1.67, 2005-01-25 15:00:33-08:00, torvalds@ppc970.osdl.org Rename "locks_verify_area()" to "rw_verify_area()" and clean up the arguments. With such range checking in place those bugs will be protected (can't be be triggered) by the higher levels - they should be fixed nevertheless. The following fixes are going into Linus's tree Wednesday, so this date (February 2) can be though of as the release date. There are for sure more sign handling bugs lying in drivers/fs'es. Kudos again to Georgi for his bright auditing! 1) drivers/char/n_tty.c::copy_from_read_buf() "nr" is user passed parameter, so if its value is higher than MAX_INT, the signed "min" will happily return "nr", causing unexpected results. Note that "n" is used afterwards as lenght parameter of copy_to_user: retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n); This fixes it as suggested by Linus: --- drivers/char/n_tty.c.orig 2005-01-20 13:33:30.000000000 -0200 +++ drivers/char/n_tty.c 2005-01-30 13:56:05.038069640 -0200 @@ -1143,13 +1143,13 @@ { int retval; - ssize_t n; + size_t n; unsigned long flags; retval = 0; spin_lock_irqsave(&tty->read_lock, flags); n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail); - n = min((ssize_t)*nr, n); + n = min(*nr, n); spin_unlock_irqrestore(&tty->read_lock, flags); if (n) { mb(); 2) fs/proc/generic.c::proc_file_read() "nbytes" is user passed parameter, so if its value is higher than MAX_INT, the signed "min" comparison returns "nbytes". Several proc handling functions do not expect huge values. get_locks_status() for example (handler for /proc/locks) can be exploited to read kernel memory. --- fs/proc/generic.c.orig 2005-01-20 12:39:42.000000000 -0200 +++ fs/proc/generic.c 2005-01-30 13:58:00.420528840 -0200 @@ -60,7 +60,7 @@ return -ENOMEM; while ((nbytes > 0) && !eof) { - count = min_t(ssize_t, PROC_BLOCK_SIZE, nbytes); + count = min_t(size_t, PROC_BLOCK_SIZE, nbytes); start = NULL; if (dp->get_info) { 3) reiserfs: NOTE: The fix is incomplete, reiserfs_copy_from_user_to_file_region() also suffers from similar issue but is not fixed in the following patch. The reiserfs people should provide a more appropriate fix. reiserfs_file_write() casts its (size_t) count parameter to int, which can become a problem on 64-bit architectures because the following range check will fail if for eg count is "0x1ffffffff": if ( unlikely((ssize_t) count < 0)) return -EINVAL; Which will later be casted to "int" here: int write_bytes; /* amount of bytes to write during this iteration */ if ( !num_pages ) { /* If we do not have enough space even for */ res = -ENOSPC; /* single page, return -ENOSPC */ if ( pos > (inode->i_size & (inode->i_sb->s_blocksize-1))) break; // In case we are writing past the file end, break. // Otherwise we are possibly overwriting the file, so // let's set write size to be equal or less than blocksize. // This way we get it correctly for file holes. // But overwriting files on absolutelly full volumes would not // be very efficient. Well, people are not supposed to fill // 100% of disk space anyway. write_bytes = min_t(int, count, inode->i_sb->s_blocksize - (pos & (inode->i_sb->s_blocksize - 1))); Making "write_bytes" negative, which potentially causes unexpected results. The following patch is an attempt to fix this by changing the variables dealing with count and offset and the "min_t" comparisons to unsigned "size_t". --- file.c.orig 2005-01-25 16:01:13.000000000 -0200 +++ file.c 2005-01-26 13:28:11.819375848 -0200 @@ -588,7 +588,7 @@ error_exit: /* Unlock pages prepared by reiserfs_prepare_file_region_for_write */ static void reiserfs_unprepare_pages(struct page **prepared_pages, /* list of locked pages */ - int num_pages /* amount of pages */) { + size_t num_pages /* amount of pages */) { int i; // loop counter for (i=0; i < num_pages ; i++) { @@ -619,7 +619,7 @@ static int reiserfs_copy_from_user_to_fi int offset; // offset in page for ( i = 0, offset = (pos & (PAGE_CACHE_SIZE-1)); i < num_pages ; i++,offset=0) { - int count = min_t(int,PAGE_CACHE_SIZE-offset,write_bytes); // How much of bytes to write to this page + size_t count = min_t(size_t,PAGE_CACHE_SIZE-offset,write_bytes); // How much of bytes to write to this page struct page *page=prepared_pages[i]; // Current page we process. fault_in_pages_readable( buf, count); @@ -718,8 +718,8 @@ static int reiserfs_submit_file_region_f struct reiserfs_transaction_handle *th, struct inode *inode, loff_t pos, /* Writing position offset */ - int num_pages, /* Number of pages to write */ - int write_bytes, /* number of bytes to write */ + size_t num_pages, /* Number of pages to write */ + size_t write_bytes, /* number of bytes to write */ struct page **prepared_pages /* list of pages */ ) { @@ -854,9 +854,9 @@ static int reiserfs_check_for_tail_and_c static int reiserfs_prepare_file_region_for_write( struct inode *inode /* Inode of the file */, loff_t pos, /* position in the file */ - int num_pages, /* number of pages to + size_t num_pages, /* number of pages to prepare */ - int write_bytes, /* Amount of bytes to be + size_t write_bytes, /* Amount of bytes to be overwritten from @pos */ struct page **prepared_pages /* pointer to array @@ -1252,10 +1252,9 @@ static ssize_t reiserfs_file_write( stru while ( count > 0) { /* This is the main loop in which we running until some error occures or until we write all of the data. */ - int num_pages;/* amount of pages we are going to write this iteration */ - int write_bytes; /* amount of bytes to write during this iteration */ - int blocks_to_allocate; /* how much blocks we need to allocate for - this iteration */ + size_t num_pages;/* amount of pages we are going to write this iteration */ + size_t write_bytes; /* amount of bytes to write during this iteration */ + size_t blocks_to_allocate; /* how much blocks we need to allocate for this iteration */ /* (pos & (PAGE_CACHE_SIZE-1)) is an idiom for offset into a page of pos*/ num_pages = !!((pos+count) & (PAGE_CACHE_SIZE - 1)) + /* round up partial @@ -1269,7 +1268,7 @@ static ssize_t reiserfs_file_write( stru /* If we were asked to write more data than we want to or if there is not that much space, then we shorten amount of data to write for this iteration. */ - num_pages = min_t(int, REISERFS_WRITE_PAGES_AT_A_TIME, reiserfs_can_fit_pages(inode->i_sb)); + num_pages = min_t(size_t, REISERFS_WRITE_PAGES_AT_A_TIME, reiserfs_can_fit_pages(inode->i_sb)); /* Also we should not forget to set size in bytes accordingly */ write_bytes = (num_pages << PAGE_CACHE_SHIFT) - (pos & (PAGE_CACHE_SIZE-1)); @@ -1295,7 +1294,7 @@ static ssize_t reiserfs_file_write( stru // But overwriting files on absolutelly full volumes would not // be very efficient. Well, people are not supposed to fill // 100% of disk space anyway. - write_bytes = min_t(int, count, inode->i_sb->s_blocksize - (pos & (inode->i_sb->s_blocksize - 1))); + write_bytes = min_t(size_t, count, inode->i_sb->s_blocksize - (pos & (inode->i_sb->s_blocksize - 1))); num_pages = 1; // No blocks were claimed before, so do it now. reiserfs_claim_blocks_to_be_allocated(inode->i_sb, 1 << (PAGE_CACHE_SHIFT - inode->i_blkbits));
<!-- SBZ_reproduce --> -
reiserfs: http://linux.bkbits.net:8080/linux-2.6/cset@42018227TkNpHlX6BefnItV_GqMmzQ
Created attachment 28196 [details] reiserfs-signed.patch from last bk chset
Created attachment 28197 [details] proc-read-signedness.patch http://linux.bkbits.net:8080/linux-2.6/cset@4201818eC6aMn0x3GY_9rw3ueb2ZWQ?nav=index.html|ChangeSet@-1d
Created attachment 28198 [details] copyfromreadbuf-signedness.patch http://linux.bkbits.net:8080/linux-2.6/cset@420181322LZmhPTewcCOLkubGwOL3w?nav=index.html|ChangeSet@-1d
Created attachment 28199 [details] proc-read-signedness.patch
Created attachment 28200 [details] reiserfs-signed.patch
Created attachment 28201 [details] fs-readwrite-signedness.patch http://linux.bkbits.net:8080/linux-2.6/cset@42026b11ti7KiDM_DMvBv5ZQH_3yLw?nav=index.html|ChangeSet@-1d
Created attachment 28202 [details] always-accessok-checks.patch http://linux.bkbits.net:8080/linux-2.6/cset@4202616998ECZp5x5NfCDbX9JcEG7g?nav=index.html|ChangeSet@-1d
Created attachment 28203 [details] verify-area-rename.patch http://linux.bkbits.net:8080/linux-2.6/cset@41f6cf91c1R7rbuggBVQLxBuD7m6Aw?nav=index.html|ChangeSet@-10d required before always-accessok-checks patch i think
Created attachment 28204 [details] f-maxcount.patch http://linux.bkbits.net:8080/linux-2.6/cset@41f71cbbbAqnp67z79i7SSVQGtmQzg?nav=index.html|ChangeSet@-10d WILL BREAK BINARY COMPATIBILITY.
We clearly can't take f-maxcount.patch, but if I'm reading things correctly, the other patches should be sufficient to fix the known bugs. I don't think we can take the verify-area-rename.patch (comment #10) without keeping locks_verify_area for compatibility. fs-readwrite-signedness.patch needs modification since we can't take the f-maxcount patch. The reiserfs change looks correct to me.
Created attachment 28290 [details] 2.4-fsmaxcount-rwverifylock.patch backport of f-maxcount and verify-area-rename patch to 2.4... just for the record.
is public now.
http://www.guninski.com/where_do_you_want_billg_to_go_today_3.html
Summary was misleading I think. These issues are triggerable from userspace without the additional VFS protections which we cannot apply.
Created attachment 28647 [details] CAN-2005-0136_ia64-kernel.diff
Date: Mon, 21 Feb 2005 12:38:20 +0100 From: Thomas Biege <thomas@suse.de> To: Thomas Biege <thomas@suse.de> Subject: [joey@infodrom.org: [vendor-sec] Re: ia64 ptrace corner cases] User-Agent: Mutt/1.5.6i ----- Forwarded message from Martin Schulze <joey@infodrom.org> ----- From: Martin Schulze <joey@infodrom.org> To: Free Software Distribution Vendors <vendor-sec@lst.de> User-Agent: Mutt/1.5.6+20040907i Subject: [vendor-sec] Re: ia64 ptrace corner cases Errors-To: vendor-sec-admin@lst.de Date: Mon, 21 Feb 2005 09:33:07 +0100 I'm a bit confused about the status of these vulnerabilities in the IA-64 kernels: CVE Id type 2.4/patch 2.6/patch public? ------------------------------------------------------------------------- CAN-2005-0135 ia64/unwind ??? ??? CVE invisible CAN-2005-0136 ia64/ptrace vendor-sec vuln, [1] CVE invisible CAN-2005-0137 ia64/syscall vendor-sec non-vuln CVE invisible [1] http://lia64.bkbits.net:8080/linux-ia64-release-2.6.11/cset@41f2d1eePludGYyb1yOmGaW6Iois8Q Are all three treated as invisible even though the fix for CAN-2005-0136 is publically available? Regards, Joey
the last comment should be somewhere else.... in https://bugzilla.innerweb.novell.com/show_bug.cgi?id=65236
yes. the patch too.
====================================================== Candidate: CAN-2005-0529 URL: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0529 Reference: FULLDISC:20050215 linux kernel 2.6 fun. windoze is a joke Reference: URL:http://marc.theaimsgroup.com/?l=full-disclosure&m=110846727602817&w=2 Reference: MISC:http://www.guninski.com/where_do_you_want_billg_to_go_today_3.html Reference: CONFIRM:http://linux.bkbits.net:8080/linux-2.6/cset@4201818eC6aMn0x3GY_9rw3ueb2ZW +Q Linux kernel 2.6.10 and 2.6.11rc1-bk6 uses different size types for offset arguments to the proc_file_read and locks_read_proc, which leads to a heap-based buffer overflow when a signed comparison causes negative integers to be used in a positive context. ====================================================== Candidate: CAN-2005-0530 URL: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0530 Reference: FULLDISC:20050215 linux kernel 2.6 fun. windoze is a joke Reference: URL:http://marc.theaimsgroup.com/?l=full-disclosure&m=110846727602817&w=2 Reference: MISC:http://www.guninski.com/where_do_you_want_billg_to_go_today_3.html Reference: CONFIRM:http://linux.bkbits.net:8080/linux-2.6/cset@420181322LZmhPTewcCOLkubGwOL3+w Signedness error in the copy_from_read_buf function in n_tty.c for Linux kernel 2.6.10 and 2.6.11rc1 allows local users to read kernel memory via a negative argument. ====================================================== Candidate: CAN-2005-0531 URL: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0531 Reference: FULLDISC:20050215 linux kernel 2.6 fun. windoze is a joke Reference: URL:http://marc.theaimsgroup.com/?l=full-disclosure&m=110846727602817&w=2 Reference: MISC:http://www.guninski.com/where_do_you_want_billg_to_go_today_3.html Reference: CONFIRM:http://linux.bkbits.net:8080/linux-2.6/gnupatch@4208e1fcfccuD-eH2OGM5mBhi +hmQ3A The atm_get_addr function in addr.c for Linux kernel 2.6.10 and 2.6.11 before 2.6.11-rc4 may allow local users to trigger a buffer overflow via negative arguments. ====================================================== Candidate: CAN-2005-0532 URL: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0532 Reference: FULLDISC:20050215 linux kernel 2.6 fun. windoze is a joke Reference: URL:http://marc.theaimsgroup.com/?l=full-disclosure&m=110846727602817&w=2 Reference: MISC:http://www.guninski.com/where_do_you_want_billg_to_go_today_3.html Reference: CONFIRM:http://linux.bkbits.net:8080/linux-2.6/cset@42018227TkNpHlX6BefnItV_GqMmz +Q The reiserfs_copy_from_user_to_file_region function in reiserfs/file.c for Linux kernel 2.6.10 and 2.6.11 before 2.6.11-rc4, when running on 64-bit architectures, may allow local users to trigger a buffer overflow as a result of casting discrepancies between size_t and int data types.
olaf to apply the first 2 patches, chris mason to apply the reiserfs patch. the infrastructure patvches will not be applied to older versions
Added patches to SLES9 SP1, SP2, and SL_92 branches. HEAD is unaffected as these patches are in 2.6.11: - patches.fixes/n-tty-signedness: Fix sign checks in copy_from_read_buf() (65318). - patches.fixes/proc-read-signedness: Fix signed compare in fs/proc/generic.c::proc_file_read() (65318). Passing this bug to Chris for reiserfs
reiserfs patches in SLES9 SP[12], and SL_92. Patches for HEAD were in 2.6.11.
reopen foir tracking
drivers/char/n_tty.c and fs/proc/generic.c are not affected in 2.4. (funny enough that this fault was introduced by introduction of the generic min() / min_t() macros in 2.6, instead of adding casts the people should have *THOUGHT* instead.) reiserfs does not have the function mentioned in 2.4, so not affected too. the others were new feature additions.
updates and advisory released
CVE-2005-0532: CVSS v2 Base Score: 2.1 (AV:L/AC:L/Au:N/C:N/I:N/A:P)