8141491: Unaligned memory access in Bits.c
Introduce alignment-safe Copy::conjoint_swap and j.i.m.Unsafe.copySwapMemory Reviewed-by: jrose, dholmes, psandoz
This commit is contained in:
parent
b4006c54e8
commit
7f395c26ab
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
@ -137,12 +137,6 @@ ifeq ($(OPENJDK_TARGET_OS), solaris)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(OPENJDK_TARGET_OS), linux)
|
||||
ifeq ($(OPENJDK_TARGET_CPU), x86_64)
|
||||
BUILD_LIBJAVA_Bits.c_CFLAGS := $(C_O_FLAG_NORM)
|
||||
endif
|
||||
endif
|
||||
|
||||
$(eval $(call SetupNativeCompilation,BUILD_LIBJAVA, \
|
||||
LIBRARY := java, \
|
||||
OUTPUT_DIR := $(INSTALL_LIBRARIES_HERE), \
|
||||
|
@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
@ -229,12 +229,6 @@ SUNWprivate_1.1 {
|
||||
Java_java_lang_Throwable_fillInStackTrace;
|
||||
Java_java_lang_Throwable_getStackTraceDepth;
|
||||
Java_java_lang_Throwable_getStackTraceElement;
|
||||
Java_java_nio_Bits_copyFromShortArray;
|
||||
Java_java_nio_Bits_copyToShortArray;
|
||||
Java_java_nio_Bits_copyFromIntArray;
|
||||
Java_java_nio_Bits_copyToIntArray;
|
||||
Java_java_nio_Bits_copyFromLongArray;
|
||||
Java_java_nio_Bits_copyToLongArray;
|
||||
Java_java_security_AccessController_doPrivileged__Ljava_security_PrivilegedAction_2;
|
||||
Java_java_security_AccessController_doPrivileged__Ljava_security_PrivilegedAction_2Ljava_security_AccessControlContext_2;
|
||||
Java_java_security_AccessController_doPrivileged__Ljava_security_PrivilegedExceptionAction_2;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -807,31 +807,131 @@ class Bits { // package-private
|
||||
}
|
||||
}
|
||||
|
||||
static void copyFromCharArray(Object src, long srcPos, long dstAddr,
|
||||
long length)
|
||||
{
|
||||
copyFromShortArray(src, srcPos, dstAddr, length);
|
||||
/**
|
||||
* Copy and unconditionally byte swap 16 bit elements from a heap array to off-heap memory
|
||||
*
|
||||
* @param src
|
||||
* the source array, must be a 16-bit primitive array type
|
||||
* @param srcPos
|
||||
* byte offset within source array of the first element to read
|
||||
* @param dstAddr
|
||||
* destination address
|
||||
* @param length
|
||||
* number of bytes to copy
|
||||
*/
|
||||
static void copyFromCharArray(Object src, long srcPos, long dstAddr, long length) {
|
||||
unsafe.copySwapMemory(src, unsafe.arrayBaseOffset(src.getClass()) + srcPos, null, dstAddr, length, 2);
|
||||
}
|
||||
|
||||
static void copyToCharArray(long srcAddr, Object dst, long dstPos,
|
||||
long length)
|
||||
{
|
||||
copyToShortArray(srcAddr, dst, dstPos, length);
|
||||
/**
|
||||
* Copy and unconditionally byte swap 16 bit elements from off-heap memory to a heap array
|
||||
*
|
||||
* @param srcAddr
|
||||
* source address
|
||||
* @param dst
|
||||
* destination array, must be a 16-bit primitive array type
|
||||
* @param dstPos
|
||||
* byte offset within the destination array of the first element to write
|
||||
* @param length
|
||||
* number of bytes to copy
|
||||
*/
|
||||
static void copyToCharArray(long srcAddr, Object dst, long dstPos, long length) {
|
||||
unsafe.copySwapMemory(null, srcAddr, dst, unsafe.arrayBaseOffset(dst.getClass()) + dstPos, length, 2);
|
||||
}
|
||||
|
||||
static native void copyFromShortArray(Object src, long srcPos, long dstAddr,
|
||||
long length);
|
||||
static native void copyToShortArray(long srcAddr, Object dst, long dstPos,
|
||||
long length);
|
||||
/**
|
||||
* Copy and unconditionally byte swap 16 bit elements from a heap array to off-heap memory
|
||||
*
|
||||
* @param src
|
||||
* the source array, must be a 16-bit primitive array type
|
||||
* @param srcPos
|
||||
* byte offset within source array of the first element to read
|
||||
* @param dstAddr
|
||||
* destination address
|
||||
* @param length
|
||||
* number of bytes to copy
|
||||
*/
|
||||
static void copyFromShortArray(Object src, long srcPos, long dstAddr, long length) {
|
||||
unsafe.copySwapMemory(src, unsafe.arrayBaseOffset(src.getClass()) + srcPos, null, dstAddr, length, 2);
|
||||
}
|
||||
|
||||
static native void copyFromIntArray(Object src, long srcPos, long dstAddr,
|
||||
long length);
|
||||
static native void copyToIntArray(long srcAddr, Object dst, long dstPos,
|
||||
long length);
|
||||
/**
|
||||
* Copy and unconditionally byte swap 16 bit elements from off-heap memory to a heap array
|
||||
*
|
||||
* @param srcAddr
|
||||
* source address
|
||||
* @param dst
|
||||
* destination array, must be a 16-bit primitive array type
|
||||
* @param dstPos
|
||||
* byte offset within the destination array of the first element to write
|
||||
* @param length
|
||||
* number of bytes to copy
|
||||
*/
|
||||
static void copyToShortArray(long srcAddr, Object dst, long dstPos, long length) {
|
||||
unsafe.copySwapMemory(null, srcAddr, dst, unsafe.arrayBaseOffset(dst.getClass()) + dstPos, length, 2);
|
||||
}
|
||||
|
||||
static native void copyFromLongArray(Object src, long srcPos, long dstAddr,
|
||||
long length);
|
||||
static native void copyToLongArray(long srcAddr, Object dst, long dstPos,
|
||||
long length);
|
||||
/**
|
||||
* Copy and unconditionally byte swap 32 bit elements from a heap array to off-heap memory
|
||||
*
|
||||
* @param src
|
||||
* the source array, must be a 32-bit primitive array type
|
||||
* @param srcPos
|
||||
* byte offset within source array of the first element to read
|
||||
* @param dstAddr
|
||||
* destination address
|
||||
* @param length
|
||||
* number of bytes to copy
|
||||
*/
|
||||
static void copyFromIntArray(Object src, long srcPos, long dstAddr, long length) {
|
||||
unsafe.copySwapMemory(src, unsafe.arrayBaseOffset(src.getClass()) + srcPos, null, dstAddr, length, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy and unconditionally byte swap 32 bit elements from off-heap memory to a heap array
|
||||
*
|
||||
* @param srcAddr
|
||||
* source address
|
||||
* @param dst
|
||||
* destination array, must be a 32-bit primitive array type
|
||||
* @param dstPos
|
||||
* byte offset within the destination array of the first element to write
|
||||
* @param length
|
||||
* number of bytes to copy
|
||||
*/
|
||||
static void copyToIntArray(long srcAddr, Object dst, long dstPos, long length) {
|
||||
unsafe.copySwapMemory(null, srcAddr, dst, unsafe.arrayBaseOffset(dst.getClass()) + dstPos, length, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy and unconditionally byte swap 64 bit elements from a heap array to off-heap memory
|
||||
*
|
||||
* @param src
|
||||
* the source array, must be a 64-bit primitive array type
|
||||
* @param srcPos
|
||||
* byte offset within source array of the first element to read
|
||||
* @param dstAddr
|
||||
* destination address
|
||||
* @param length
|
||||
* number of bytes to copy
|
||||
*/
|
||||
static void copyFromLongArray(Object src, long srcPos, long dstAddr, long length) {
|
||||
unsafe.copySwapMemory(src, unsafe.arrayBaseOffset(src.getClass()) + srcPos, null, dstAddr, length, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy and unconditionally byte swap 64 bit elements from off-heap memory to a heap array
|
||||
*
|
||||
* @param srcAddr
|
||||
* source address
|
||||
* @param dst
|
||||
* destination array, must be a 64-bit primitive array type
|
||||
* @param dstPos
|
||||
* byte offset within the destination array of the first element to write
|
||||
* @param length
|
||||
* number of bytes to copy
|
||||
*/
|
||||
static void copyToLongArray(long srcAddr, Object dst, long dstPos, long length) {
|
||||
unsafe.copySwapMemory(null, srcAddr, dst, unsafe.arrayBaseOffset(dst.getClass()) + dstPos, length, 8);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -458,6 +458,78 @@ public final class Unsafe {
|
||||
copyMemory(null, srcAddress, null, destAddress, bytes);
|
||||
}
|
||||
|
||||
private boolean isPrimitiveArray(Class<?> c) {
|
||||
Class<?> componentType = c.getComponentType();
|
||||
return componentType != null && componentType.isPrimitive();
|
||||
}
|
||||
|
||||
private native void copySwapMemory0(Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes, long elemSize);
|
||||
|
||||
/**
|
||||
* Copies all elements from one block of memory to another block,
|
||||
* *unconditionally* byte swapping the elements on the fly.
|
||||
*
|
||||
* <p>This method determines each block's base address by means of two parameters,
|
||||
* and so it provides (in effect) a <em>double-register</em> addressing mode,
|
||||
* as discussed in {@link #getInt(Object,long)}. When the object reference is null,
|
||||
* the offset supplies an absolute base address.
|
||||
*
|
||||
* @since 9
|
||||
*/
|
||||
public void copySwapMemory(Object srcBase, long srcOffset,
|
||||
Object destBase, long destOffset,
|
||||
long bytes, long elemSize) {
|
||||
if (bytes < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (elemSize != 2 && elemSize != 4 && elemSize != 8) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (bytes % elemSize != 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if ((srcBase == null && srcOffset == 0) ||
|
||||
(destBase == null && destOffset == 0)) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
// Must be off-heap, or primitive heap arrays
|
||||
if (srcBase != null && (srcOffset < 0 || !isPrimitiveArray(srcBase.getClass()))) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
if (destBase != null && (destOffset < 0 || !isPrimitiveArray(destBase.getClass()))) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
// Sanity check size and offsets on 32-bit platforms. Most
|
||||
// significant 32 bits must be zero.
|
||||
if (ADDRESS_SIZE == 4 &&
|
||||
(bytes >>> 32 != 0 || srcOffset >>> 32 != 0 || destOffset >>> 32 != 0)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if (bytes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
copySwapMemory0(srcBase, srcOffset, destBase, destOffset, bytes, elemSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all elements from one block of memory to another block, byte swapping the
|
||||
* elements on the fly.
|
||||
*
|
||||
* This provides a <em>single-register</em> addressing mode, as
|
||||
* discussed in {@link #getInt(Object,long)}.
|
||||
*
|
||||
* Equivalent to {@code copySwapMemory(null, srcAddress, null, destAddress, bytes, elemSize)}.
|
||||
*/
|
||||
public void copySwapMemory(long srcAddress, long destAddress, long bytes, long elemSize) {
|
||||
copySwapMemory(null, srcAddress, null, destAddress, bytes, elemSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes of a block of native memory, as obtained from {@link
|
||||
* #allocateMemory} or {@link #reallocateMemory}. The address passed to
|
||||
|
@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
#include "jni.h"
|
||||
#include "jni_util.h"
|
||||
#include "jlong.h"
|
||||
#include <string.h>
|
||||
|
||||
#define MBYTE 1048576
|
||||
|
||||
#define GETCRITICAL_OR_RETURN(bytes, env, obj) { \
|
||||
bytes = (*env)->GetPrimitiveArrayCritical(env, obj, NULL); \
|
||||
if (bytes == NULL) { \
|
||||
if ((*env)->ExceptionOccurred(env) == NULL) \
|
||||
JNU_ThrowInternalError(env, "Unable to get array"); \
|
||||
return; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define RELEASECRITICAL(bytes, env, obj, mode) { \
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, obj, bytes, mode); \
|
||||
}
|
||||
|
||||
#define SWAPSHORT(x) ((jshort)(((x) << 8) | (((x) >> 8) & 0xff)))
|
||||
#define SWAPINT(x) ((jint)((SWAPSHORT((jshort)(x)) << 16) | \
|
||||
(SWAPSHORT((jshort)((x) >> 16)) & 0xffff)))
|
||||
#define SWAPLONG(x) ((jlong)(((jlong)SWAPINT((jint)(x)) << 32) | \
|
||||
((jlong)SWAPINT((jint)((x) >> 32)) & 0xffffffff)))
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_nio_Bits_copyFromShortArray(JNIEnv *env, jclass clazz, jobject src,
|
||||
jlong srcPos, jlong dstAddr, jlong length)
|
||||
{
|
||||
jbyte *bytes;
|
||||
size_t size;
|
||||
jshort *srcShort, *dstShort, *endShort;
|
||||
jshort tmpShort;
|
||||
|
||||
dstShort = (jshort *)jlong_to_ptr(dstAddr);
|
||||
|
||||
while (length > 0) {
|
||||
size = (length < MBYTE) ? (size_t)length : (size_t)MBYTE;
|
||||
|
||||
GETCRITICAL_OR_RETURN(bytes, env, src);
|
||||
|
||||
srcShort = (jshort *)(bytes + srcPos);
|
||||
endShort = srcShort + (size / sizeof(jshort));
|
||||
while (srcShort < endShort) {
|
||||
tmpShort = *srcShort++;
|
||||
*dstShort++ = SWAPSHORT(tmpShort);
|
||||
}
|
||||
|
||||
RELEASECRITICAL(bytes, env, src, JNI_ABORT);
|
||||
|
||||
length -= size;
|
||||
srcPos += size;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_nio_Bits_copyToShortArray(JNIEnv *env, jclass clazz, jlong srcAddr,
|
||||
jobject dst, jlong dstPos, jlong length)
|
||||
{
|
||||
jbyte *bytes;
|
||||
size_t size;
|
||||
jshort *srcShort, *dstShort, *endShort;
|
||||
jshort tmpShort;
|
||||
|
||||
srcShort = (jshort *)jlong_to_ptr(srcAddr);
|
||||
|
||||
while (length > 0) {
|
||||
size = (length < MBYTE) ? (size_t)length : (size_t)MBYTE;
|
||||
|
||||
GETCRITICAL_OR_RETURN(bytes, env, dst);
|
||||
|
||||
dstShort = (jshort *)(bytes + dstPos);
|
||||
endShort = srcShort + (size / sizeof(jshort));
|
||||
while (srcShort < endShort) {
|
||||
tmpShort = *srcShort++;
|
||||
*dstShort++ = SWAPSHORT(tmpShort);
|
||||
}
|
||||
|
||||
RELEASECRITICAL(bytes, env, dst, 0);
|
||||
|
||||
length -= size;
|
||||
dstPos += size;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_nio_Bits_copyFromIntArray(JNIEnv *env, jclass clazz, jobject src,
|
||||
jlong srcPos, jlong dstAddr, jlong length)
|
||||
{
|
||||
jbyte *bytes;
|
||||
size_t size;
|
||||
jint *srcInt, *dstInt, *endInt;
|
||||
jint tmpInt;
|
||||
|
||||
dstInt = (jint *)jlong_to_ptr(dstAddr);
|
||||
|
||||
while (length > 0) {
|
||||
size = (length < MBYTE) ? (size_t)length : (size_t)MBYTE;
|
||||
|
||||
GETCRITICAL_OR_RETURN(bytes, env, src);
|
||||
|
||||
srcInt = (jint *)(bytes + srcPos);
|
||||
endInt = srcInt + (size / sizeof(jint));
|
||||
while (srcInt < endInt) {
|
||||
tmpInt = *srcInt++;
|
||||
*dstInt++ = SWAPINT(tmpInt);
|
||||
}
|
||||
|
||||
RELEASECRITICAL(bytes, env, src, JNI_ABORT);
|
||||
|
||||
length -= size;
|
||||
srcPos += size;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_nio_Bits_copyToIntArray(JNIEnv *env, jclass clazz, jlong srcAddr,
|
||||
jobject dst, jlong dstPos, jlong length)
|
||||
{
|
||||
jbyte *bytes;
|
||||
size_t size;
|
||||
jint *srcInt, *dstInt, *endInt;
|
||||
jint tmpInt;
|
||||
|
||||
srcInt = (jint *)jlong_to_ptr(srcAddr);
|
||||
|
||||
while (length > 0) {
|
||||
size = (length < MBYTE) ? (size_t)length : (size_t)MBYTE;
|
||||
|
||||
GETCRITICAL_OR_RETURN(bytes, env, dst);
|
||||
|
||||
dstInt = (jint *)(bytes + dstPos);
|
||||
endInt = srcInt + (size / sizeof(jint));
|
||||
while (srcInt < endInt) {
|
||||
tmpInt = *srcInt++;
|
||||
*dstInt++ = SWAPINT(tmpInt);
|
||||
}
|
||||
|
||||
RELEASECRITICAL(bytes, env, dst, 0);
|
||||
|
||||
length -= size;
|
||||
dstPos += size;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_nio_Bits_copyFromLongArray(JNIEnv *env, jclass clazz, jobject src,
|
||||
jlong srcPos, jlong dstAddr, jlong length)
|
||||
{
|
||||
jbyte *bytes;
|
||||
size_t size;
|
||||
jlong *srcLong, *dstLong, *endLong;
|
||||
jlong tmpLong;
|
||||
|
||||
dstLong = (jlong *)jlong_to_ptr(dstAddr);
|
||||
|
||||
while (length > 0) {
|
||||
size = (length < MBYTE) ? (size_t)length : (size_t)MBYTE;
|
||||
|
||||
GETCRITICAL_OR_RETURN(bytes, env, src);
|
||||
|
||||
srcLong = (jlong *)(bytes + srcPos);
|
||||
endLong = srcLong + (size / sizeof(jlong));
|
||||
while (srcLong < endLong) {
|
||||
tmpLong = *srcLong++;
|
||||
*dstLong++ = SWAPLONG(tmpLong);
|
||||
}
|
||||
|
||||
RELEASECRITICAL(bytes, env, src, JNI_ABORT);
|
||||
|
||||
length -= size;
|
||||
srcPos += size;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_nio_Bits_copyToLongArray(JNIEnv *env, jclass clazz, jlong srcAddr,
|
||||
jobject dst, jlong dstPos, jlong length)
|
||||
{
|
||||
jbyte *bytes;
|
||||
size_t size;
|
||||
jlong *srcLong, *dstLong, *endLong;
|
||||
jlong tmpLong;
|
||||
|
||||
srcLong = (jlong *)jlong_to_ptr(srcAddr);
|
||||
|
||||
while (length > 0) {
|
||||
size = (length < MBYTE) ? (size_t)length : (size_t)MBYTE;
|
||||
|
||||
GETCRITICAL_OR_RETURN(bytes, env, dst);
|
||||
|
||||
dstLong = (jlong *)(bytes + dstPos);
|
||||
endLong = srcLong + (size / sizeof(jlong));
|
||||
while (srcLong < endLong) {
|
||||
tmpLong = *srcLong++;
|
||||
*dstLong++ = SWAPLONG(tmpLong);
|
||||
}
|
||||
|
||||
RELEASECRITICAL(bytes, env, dst, 0);
|
||||
|
||||
length -= size;
|
||||
dstPos += size;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user