代码质量不佳会导致不可预测的行为。对于用户来说,通常表现为可用性差。对于攻击者来说,提供了以意外方式对系统施加压力的机会。
getChunk == NULL
将永远是 false,因为 getChunk
是程序中定义的一个函数名称。
if (getChunk == NULL)
return ERR;
char* getName() {
char name[STR_MAX];
fillInName(name);
return name;
}
class AccessLevel{
public static final int ROOT = 0;
//...
public static final int NONE = 9;
}
//...
class User {
private static int access;
public User(){
access = AccessLevel.ROOT;
}
public static int getAccessLevel(){
return access;
}
//...
}
class RegularUser extends User {
private static int access;
public RegularUser(){
access = AccessLevel.NONE;
}
public static int getAccessLevel(){
return access;
}
public static void escalatePrivilege(){
access = AccessLevel.ROOT;
}
//...
}
//...
class SecureArea {
//...
public static void doRestrictedOperation(User user){
if (user instanceof RegularUser){
if (user.getAccessLevel() == AccessLevel.ROOT){
System.out.println("doing a privileged operation");
}else{
throw new RuntimeException();
}
}
}
}
user
实例,而非 User
或 RegularUser
类来调用 getAccessLevel()
方法,这意味着此条件下将始终返回 true
且会执行该限制操作,即使使用了 instanceof
以便进入 if/else
块的此部分也是如此。serialPersistentFields
,必须将其声明为 private
、static
和 final
。serialPersistentFields
数组中指定类的可序列化的字段来手动定义这些字段。仅当 serialPersistentFields
被声明为 private
、static
和 final
时,此功能才能运行。serialPersistentFields
的声明将不会用来定义 Serializable
字段,因为它不是 private
、static
和 final
。
class List implements Serializable {
public ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("myField", List.class) };
...
}
Object.equals()
,而非 java.util.Arrays.equals().
Object.equals()
会检查数组地址是否相同而非检查数组元素是否相同,因此在大多数情况下这是一个错误调用,通常应将该代码替换为 java.util.Arrays.equals()
。 Object.equals()
函数检查两个数组。
...
int[] arr1 = new int[10];
int[] arr2 = new int[10];
...
if (arr1.equals(arr2)){
//treat arrays as if identical elements
}
...
pthread_cleanup_push()
函数将 routine
函数推至调用线程清除堆栈之上,然后返回。由于 pthread_cleanup_push()
及其搭档函数 pthread_cleanup_pop()
在 IBM AIX 之外的平台上作为宏来执行,因此随后调用 pthread_cleanup_pop()
将无法访问 pthread_cleanup_push()
创建的数据结构。在将这些函数作为宏执行的平台上,该代码将无法编译,或者不能正确运行。
void helper() {
...
pthread_cleanup_push (routine, arg);
}
void clean_up()
{
char tmp[256];
...
free(tmp);
return;
}
System.Object.Equals()
:
public boolean Equals(string obj) {
...
}
System.Object.Equals()
采用 object
类型的参数,因此永远不会调用该方法。Object.equals()
:
public boolean equals(Object obj1, Object obj2) {
...
}
Object.equals()
只需要一个参数,因此永远不会调用Example 1
中的方法。ISerializable
接口但没有声明 [Serializable]
属性的类无法序列化。[Serializable]
属性的对象进行序列化。如果能使用由 .NET 框架定义的默认序列化方法对一个类进行序列化,那么其对象也必定能正确序列化。如果该类需要用自定义的序列化方法,则它还必须实施 ISerializable
接口。然而,该类仍必须声明 [Serializable]
属性。CustomStorage
实施了 ISerializable
接口。但是由于它声明 [Serializable]
属性失败,因此将不能被序列化。
public class CustomStorage: ISerializable {
...
}
java.io.Serializable
的内部类可能会导致问题以及泄露外部类中的信息。
...
class User implements Serializable {
private int accessLevel;
class Registrator implements Serializable {
...
}
}
Example 1
中,对内部类 Registrator
执行序列化后,也会对外部类 User
的 accessLevel
字段执行序列化。synchronized
,以保证当多个线程访问相同实例时的正确行为。应将所有重写方法声明为 synchronized
,否则可能会发生意外行为。Foo
覆盖类 Bar
,但未将方法 synchronizedMethod
声明为 synchronized
:
public class Bar {
public synchronized void synchronizedMethod() {
for (int i=0; i<10; i++) System.out.print(i);
System.out.println();
}
}
public class Foo extends Bar {
public void synchronizedMethod() {
for (int i=0; i<10; i++) System.out.print(i);
System.out.println();
}
}
Foo
实例会被转换为 Bar
类型。如果将相同的实例交给两个独立线程,并重复执行 synchronizedMethod
,则行为将不可预知。obj.Equals(null)
将始终为 false。Equals()
方法将一个对象与 null
作比较。Equals()
方法的约定要求这一比较过程始终返回 false。obj.equals(null)
将总是 false。equals()
方法将一个对象与 null
进行比较。这种比较将始终返回 false,因为该对象并不是 null
。(如果对象为 null
,则程序将抛出 NullPointerException
异常)。main()
函数调用 pthread_create()
而产生的线程,如果父进程没有调用 pthread_exit()
而早于任何线程结束执行,则这些线程会过早终止。调用 pthread_exit()
可保证在其所有线程执行完毕之前,父进程保持活动状态。此外,父进程可以调用所有子线程上的 pthread_join
,并且确保它们将在进程结束之前完成。pthread_create()
函数创建一个线程,然后正常退出。如果子线程在 main()
函数返回时仍未结束执行,则该线程会被过早地终止。
void *Simple(void *threadid)
{
...
pthread_exit(NULL);
}
int main(int argc, char *argv[]) {
int rc;
pthread_t pt;
rc = pthread_create(&pt, NULL, Simple, (void *)t);
if (rc){
exit(-1);
}
}
readObject()
方法会调用可能被覆盖的函数。readObject()
充当构造函数,因此到此函数终止时,对象初始化才会完成。因此,如果 Serializable
类的 readObject()
函数调用了可覆盖的函数,则在对象尚未完成初始化之前,可能会提供对象状态的覆盖方法访问权限。readObject()
函数调用了可覆盖的方法。
...
private void readObject(final ObjectInputStream ois) throws IOException, ClassNotFoundException {
checkStream(ois);
ois.defaultReadObject();
}
public void checkStream(ObjectInputStream stream){
...
}
checkStream()
和其封装类并非 final
和公共字段,则意味着该函数是可覆盖的,这意味着攻击者可以覆盖 checkStream()
函数,以便在反序列化过程中访问对象。private readonly
列表变量,您可以调用代码来修改该列表的内容,这样,可有效提供列表的写访问权限,并阻止程序员将其设置为 private readonly
的计划。private readonly
的列表 _item
。
class Order
{
private readonly List<string> _item = new List<string>();
public IEnumerable<string> Item { get { return _item; } }
public Order()
{
/*class initialize */
}
/*some important function......*/
}
Marker child = MarkerManager.getMarker("child");
Marker parent = MarkerManager.getMarker("parent");
child.addParents(parent);
parent.addParents(child);
String toInfinity = child.toString();
toString()
时,会触发堆栈溢出异常(堆栈耗尽)。此异常是由于 child 和 parent 之间存在循环链接而导致的。String
对象进行对比非常不可靠,不应该这样做。String
对象进行比较,则必须先将该值更改为 String
对象,通常是通过如 Double.toString()
等函数来实现。在将浮点变量转换为 String
对象后,其可能为 "NaN"、"Infinity" 或 "-Infinity",或者带有几位小数点(其中包含 0),或者可能包含指数字段,具体取决于浮点变量的类型和数值。如果转换为十六进制字符串,则其形式也会有很大差异。String
进行了比较。
...
int initialNum = 1;
...
String resultString = Double.valueOf(initialNum/10000.0).toString();
if (s.equals("0.0001")){
//do something
...
}
...
if
语句的条件无法得到满足。这需要变量 s
为非 null 变量,且仅在可以将 s
指定为非 null 值的路径时,存在 return
语句。
String s = null;
if (b) {
s = "Yes";
return;
}
if (s != null) {
Dead();
}