Hi,

not sure where the bug is - gcc 4.2.4pre (CVS), binutils 2.17,
cross compiler X86_64 -> ARM BE.

-O2 -fno-strict-aliasing -fno-common -fno-stack-protector -marm
-fno-omit-frame-pointer -mapcs -mno-sched-prolog -mabi=apcs-gnu
-mno-thumb-interwork -march=armv5te -mtune=xscale -mcpu=xscale
-msoft-float -Uarm -fno-omit-frame-pointer -fno-optimize-sibling-calls

Building a test Linux kernel module:

#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>

#define USE_UACCESS 0

#if USE_UACCESS
#include <asm/uaccess.h>
#else
extern int __get_user_1(void *);

/*
        .global __get_user_1
__get_user_1:
1:      ldrbt   r2, [r0]
        mov     r0, #0
        mov     pc, lr
*/

#define get_user(x,p)                                                   \
        ({                                                              \
                register const u8 __user *__p asm("r0") = (p);          \
                register unsigned long __r2 asm("r2");                  \
                register int __e asm("r0");                             \
                __asm__ __volatile__ (                                  \
                        __asmeq("%0", "r0") __asmeq("%1", "r2")         \
                        "bl     __get_user_1"                           \
                        : "=&r" (__e), "=r" (__r2)                      \
                        : "0" (__p)                                     \
                        : "lr", "cc");                                  \
                x = (u8) __r2;                                          \
                __e;                                                    \
        })

#endif

struct port {
        u8 chan_buf[256];
        unsigned int tx_count;
        u8 modulo;
        struct cdev cdev;
        struct device *dev;
};

static struct class *test_class;
static dev_t rdev;
static struct port *main_port;

static ssize_t test_chan_write(struct file *file, const char __user *buf,
                               size_t count, loff_t *f_pos)
{
        struct port *port = main_port;
        int res = 0;
        unsigned int tail, chan, frame;

        tail = port->tx_count % 2;
        chan = tail % port->modulo;
        frame = tail / port->modulo;

        if (get_user(port->chan_buf[chan * 2 + frame], buf))
                return -EFAULT;
        port->tx_count++;
        res++;
        return res;
}

static const struct file_operations chan_fops = {
        .owner   = THIS_MODULE,
        .llseek  = no_llseek,
        .write   = test_chan_write,
};


static int __init test_init_module(void)
{
        int err;

        if ((err = alloc_chrdev_region(&rdev, 0, 1, "test")))
                return err;

        if (IS_ERR(test_class = class_create(THIS_MODULE, "test"))) {
                printk(KERN_ERR "Can't register device class 'test'\n");
                err = PTR_ERR(test_class);
                goto free_chrdev;
        }

        if (!(main_port = kzalloc(sizeof(*main_port), GFP_KERNEL))) {
                err = -ENOBUFS;
                goto destroy_class;
        }

        main_port->dev = device_create(test_class, NULL, rdev, "test");
        if (IS_ERR(main_port->dev)) {
                err = PTR_ERR(main_port->dev);
                goto free;
        }

        main_port->tx_count = 0;
        main_port->modulo = 2;

        cdev_init(&main_port->cdev, &chan_fops);
        main_port->cdev.owner = THIS_MODULE;
        if ((err = cdev_add(&main_port->cdev, rdev, 1)))
                goto destroy_device;

        dev_set_drvdata(main_port->dev, &main_port);

        printk(KERN_CRIT "start\n");
        return 0;

destroy_device:
        device_unregister(main_port->dev);
free:
        kfree(main_port);
destroy_class:
        class_destroy(test_class);
free_chrdev:
        unregister_chrdev_region(rdev, 1);
        return err;
}

static void __exit test_cleanup_module(void)
{
        printk(KERN_CRIT "tx_count = %u, modulo = %u\n",
               main_port->tx_count, main_port->modulo);
        cdev_del(&main_port->cdev);
        device_unregister(main_port->dev);
        kfree(main_port);
        class_destroy(test_class);
        unregister_chrdev_region(rdev, 1);
}

MODULE_LICENSE("GPL v2");
module_init(test_init_module);
module_exit(test_cleanup_module);

# Makefile
obj-m   := ixp-test.o
default:
        make -C /usr/local/build/diskless/xscale_be-router-test-linux 
SUBDIRS=`pwd` ARCH=arm CROSS_COMPILE=armeb-pc-linux-gnu- modules

gcc produces the following assembly code:

00000000 <test_chan_write>:
   0:   e1a0c00d        mov     ip, sp
   4:   e92dddf0        stmdb   sp!, {r4, r5, r6, r7, r8, sl, fp, ip, lr, pc}
   8:   e24cb004        sub     fp, ip, #4      ; 0x4
   c:   e59f3054        ldr     r3, [pc, #84]   ; 68 <.text+0x68>
  10:   e1a00001        mov     r0, r1          /* buf */
  14:   e5938000        ldr     r8, [r3]
  18:   e598a100        ldr     sl, [r8, #256]  /* port->tx_count */
  1c:   e5d86104        ldrb    r6, [r8, #260]  /* port->modulo */
  20:   e20a4001        and     r4, sl, #1      ; 0x1

  24:   ebfffffe        bl      0 <__get_user_1> /* returns value in R0 */
  28:   e1a01006        mov     r1, r6
  2c:   e1a00004        mov     r0, r4          /* R0 overwritten here */
  30:   e1a07002        mov     r7, r2
  34:   ebfffffe        bl      0 <__umodsi3>
  38:   e1a01006        mov     r1, r6
  3c:   e1a05000        mov     r5, r0
  40:   e1a00004        mov     r0, r4
  44:   ebfffffe        bl      0 <__udivsi3>

/* gcc now tests already overwritten __get_user_1() return value here */
  48:   e3500000        cmp     r0, #0  ; 0x0
  4c:   e0800085        add     r0, r0, r5, lsl #1
  50:   e7c07008        strb    r7, [r0, r8]
  54:   028a3001        addeq   r3, sl, #1      ; 0x1
  58:   13e0000d        mvnne   r0, #13 ; 0xd
  5c:   03a00001        moveq   r0, #1  ; 0x1
  60:   05883100        streq   r3, [r8, #256]
  64:   e89dadf0        ldmia   sp, {r4, r5, r6, r7, r8, sl, fp, sp, pc}
  68:   00000008        andeq   r0, r0, r8

Gcc bug? get_user() bug? Should I file a bug entry?

Gcc-4.1.2 and 4.1-CVS do a similar thing.
-- 
Krzysztof Halasa

Reply via email to