limit number of pending vblank signals to 100 to prevent DoS, and minor

cleanups
main
Michel Daenzer 2003-01-11 20:58:20 +00:00
parent 6f940bc3f8
commit 826aad0aba
4 changed files with 82 additions and 28 deletions

View File

@ -592,6 +592,7 @@ typedef struct drm_device {
atomic_t vbl_received; atomic_t vbl_received;
spinlock_t vbl_lock; spinlock_t vbl_lock;
drm_vbl_sig_t vbl_sigs; drm_vbl_sig_t vbl_sigs;
unsigned int vbl_pending;
#endif #endif
cycles_t ctx_start; cycles_t ctx_start;
cycles_t lck_start; cycles_t lck_start;

View File

@ -544,6 +544,8 @@ int DRM(irq_install)( drm_device_t *dev, int irq )
spin_lock_init( &dev->vbl_lock ); spin_lock_init( &dev->vbl_lock );
INIT_LIST_HEAD( &dev->vbl_sigs.head ); INIT_LIST_HEAD( &dev->vbl_sigs.head );
dev->vbl_pending = 0;
#endif #endif
/* Before installing handler */ /* Before installing handler */
@ -636,10 +638,38 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS )
if ( flags & _DRM_VBLANK_SIGNAL ) { if ( flags & _DRM_VBLANK_SIGNAL ) {
unsigned long irqflags; unsigned long irqflags;
drm_vbl_sig_t *vbl_sig = DRM_MALLOC( sizeof( drm_vbl_sig_t ) ); drm_vbl_sig_t *vbl_sig;
if ( !vbl_sig ) vblwait.reply.sequence = atomic_read( &dev->vbl_received );
spin_lock_irqsave( &dev->vbl_lock, irqflags );
/* Check if this task has already scheduled the same signal
* for the same vblank sequence number; nothing to be done in
* that case
*/
list_for_each( ( (struct list_head *) vbl_sig ), &dev->vbl_sigs.head ) {
if (vbl_sig->sequence == vblwait.request.sequence
&& vbl_sig->info.si_signo == vblwait.request.signal
&& vbl_sig->task == current)
{
spin_unlock_irqrestore( &dev->vbl_lock, irqflags );
goto done;
}
}
if ( dev->vbl_pending >= 100 ) {
spin_unlock_irqrestore( &dev->vbl_lock, irqflags );
return -EBUSY;
}
dev->vbl_pending++;
spin_unlock_irqrestore( &dev->vbl_lock, irqflags );
if ( !( vbl_sig = DRM_MALLOC( sizeof( drm_vbl_sig_t ) ) ) ) {
return -ENOMEM; return -ENOMEM;
}
memset( (void *)vbl_sig, 0, sizeof(*vbl_sig) ); memset( (void *)vbl_sig, 0, sizeof(*vbl_sig) );
@ -647,9 +677,6 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS )
vbl_sig->info.si_signo = vblwait.request.signal; vbl_sig->info.si_signo = vblwait.request.signal;
vbl_sig->task = current; vbl_sig->task = current;
vblwait.reply.sequence = atomic_read( &dev->vbl_received );
/* Hook signal entry into list */
spin_lock_irqsave( &dev->vbl_lock, irqflags ); spin_lock_irqsave( &dev->vbl_lock, irqflags );
list_add_tail( (struct list_head *) vbl_sig, &dev->vbl_sigs.head ); list_add_tail( (struct list_head *) vbl_sig, &dev->vbl_sigs.head );
@ -663,6 +690,7 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS )
vblwait.reply.tval_usec = now.tv_usec; vblwait.reply.tval_usec = now.tv_usec;
} }
done:
DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait, DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait,
sizeof(vblwait) ); sizeof(vblwait) );
@ -671,25 +699,23 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS )
void DRM(vbl_send_signals)( drm_device_t *dev ) void DRM(vbl_send_signals)( drm_device_t *dev )
{ {
struct list_head *entry, *tmp; struct list_head *tmp;
drm_vbl_sig_t *vbl_sig; drm_vbl_sig_t *vbl_sig;
unsigned int vbl_seq = atomic_read( &dev->vbl_received ); unsigned int vbl_seq = atomic_read( &dev->vbl_received );
unsigned long flags; unsigned long flags;
spin_lock_irqsave( &dev->vbl_lock, flags ); spin_lock_irqsave( &dev->vbl_lock, flags );
list_for_each_safe( entry, tmp, &dev->vbl_sigs.head ) { list_for_each_safe( ( (struct list_head *) vbl_sig ), tmp, &dev->vbl_sigs.head ) {
vbl_sig = (drm_vbl_sig_t *) entry;
if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) { if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) {
vbl_sig->info.si_code = vbl_seq;
vbl_sig->info.si_code = atomic_read( &dev->vbl_received );
send_sig_info( vbl_sig->info.si_signo, &vbl_sig->info, vbl_sig->task ); send_sig_info( vbl_sig->info.si_signo, &vbl_sig->info, vbl_sig->task );
list_del( entry ); list_del( (struct list_head *) vbl_sig );
DRM_FREE( entry ); DRM_FREE( vbl_sig );
dev->vbl_pending--;
} }
} }

View File

@ -592,6 +592,7 @@ typedef struct drm_device {
atomic_t vbl_received; atomic_t vbl_received;
spinlock_t vbl_lock; spinlock_t vbl_lock;
drm_vbl_sig_t vbl_sigs; drm_vbl_sig_t vbl_sigs;
unsigned int vbl_pending;
#endif #endif
cycles_t ctx_start; cycles_t ctx_start;
cycles_t lck_start; cycles_t lck_start;

View File

@ -544,6 +544,8 @@ int DRM(irq_install)( drm_device_t *dev, int irq )
spin_lock_init( &dev->vbl_lock ); spin_lock_init( &dev->vbl_lock );
INIT_LIST_HEAD( &dev->vbl_sigs.head ); INIT_LIST_HEAD( &dev->vbl_sigs.head );
dev->vbl_pending = 0;
#endif #endif
/* Before installing handler */ /* Before installing handler */
@ -636,10 +638,38 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS )
if ( flags & _DRM_VBLANK_SIGNAL ) { if ( flags & _DRM_VBLANK_SIGNAL ) {
unsigned long irqflags; unsigned long irqflags;
drm_vbl_sig_t *vbl_sig = DRM_MALLOC( sizeof( drm_vbl_sig_t ) ); drm_vbl_sig_t *vbl_sig;
if ( !vbl_sig ) vblwait.reply.sequence = atomic_read( &dev->vbl_received );
spin_lock_irqsave( &dev->vbl_lock, irqflags );
/* Check if this task has already scheduled the same signal
* for the same vblank sequence number; nothing to be done in
* that case
*/
list_for_each( ( (struct list_head *) vbl_sig ), &dev->vbl_sigs.head ) {
if (vbl_sig->sequence == vblwait.request.sequence
&& vbl_sig->info.si_signo == vblwait.request.signal
&& vbl_sig->task == current)
{
spin_unlock_irqrestore( &dev->vbl_lock, irqflags );
goto done;
}
}
if ( dev->vbl_pending >= 100 ) {
spin_unlock_irqrestore( &dev->vbl_lock, irqflags );
return -EBUSY;
}
dev->vbl_pending++;
spin_unlock_irqrestore( &dev->vbl_lock, irqflags );
if ( !( vbl_sig = DRM_MALLOC( sizeof( drm_vbl_sig_t ) ) ) ) {
return -ENOMEM; return -ENOMEM;
}
memset( (void *)vbl_sig, 0, sizeof(*vbl_sig) ); memset( (void *)vbl_sig, 0, sizeof(*vbl_sig) );
@ -647,9 +677,6 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS )
vbl_sig->info.si_signo = vblwait.request.signal; vbl_sig->info.si_signo = vblwait.request.signal;
vbl_sig->task = current; vbl_sig->task = current;
vblwait.reply.sequence = atomic_read( &dev->vbl_received );
/* Hook signal entry into list */
spin_lock_irqsave( &dev->vbl_lock, irqflags ); spin_lock_irqsave( &dev->vbl_lock, irqflags );
list_add_tail( (struct list_head *) vbl_sig, &dev->vbl_sigs.head ); list_add_tail( (struct list_head *) vbl_sig, &dev->vbl_sigs.head );
@ -663,6 +690,7 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS )
vblwait.reply.tval_usec = now.tv_usec; vblwait.reply.tval_usec = now.tv_usec;
} }
done:
DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait, DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait,
sizeof(vblwait) ); sizeof(vblwait) );
@ -671,25 +699,23 @@ int DRM(wait_vblank)( DRM_IOCTL_ARGS )
void DRM(vbl_send_signals)( drm_device_t *dev ) void DRM(vbl_send_signals)( drm_device_t *dev )
{ {
struct list_head *entry, *tmp; struct list_head *tmp;
drm_vbl_sig_t *vbl_sig; drm_vbl_sig_t *vbl_sig;
unsigned int vbl_seq = atomic_read( &dev->vbl_received ); unsigned int vbl_seq = atomic_read( &dev->vbl_received );
unsigned long flags; unsigned long flags;
spin_lock_irqsave( &dev->vbl_lock, flags ); spin_lock_irqsave( &dev->vbl_lock, flags );
list_for_each_safe( entry, tmp, &dev->vbl_sigs.head ) { list_for_each_safe( ( (struct list_head *) vbl_sig ), tmp, &dev->vbl_sigs.head ) {
vbl_sig = (drm_vbl_sig_t *) entry;
if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) { if ( ( vbl_seq - vbl_sig->sequence ) <= (1<<23) ) {
vbl_sig->info.si_code = vbl_seq;
vbl_sig->info.si_code = atomic_read( &dev->vbl_received );
send_sig_info( vbl_sig->info.si_signo, &vbl_sig->info, vbl_sig->task ); send_sig_info( vbl_sig->info.si_signo, &vbl_sig->info, vbl_sig->task );
list_del( entry ); list_del( (struct list_head *) vbl_sig );
DRM_FREE( entry ); DRM_FREE( vbl_sig );
dev->vbl_pending--;
} }
} }