代码质量不佳会导致不可预测的行为。对于用户来说,通常表现为可用性差。对于攻击者来说,提供了以意外方式对系统施加压力的机会。
...
var file:File = new File(directoryName + "\\" + fileName);
...
...
FileStream f = File.Create(directoryName + "\\" + fileName);
...
...
File file = new File(directoryName + "\\" + fileName);
...
...
os.open(directoryName + "\\" + fileName);
...
<script>
标签。
...
public String tagProcessor(String tag){
if (tag.toUpperCase().equals("SCRIPT")){
return null;
}
//does not contain SCRIPT tag, keep processing input
...
}
...
Example 1
的问题是,在使用不带区域设置的 java.lang.String.toUpperCase()
时,它将使用默认区域设置的规则。使用土耳其区域设置 "title".toUpperCase()
时将返回“T\u0130TLE”,其中“\u0130”是“LATIN CAPITAL LETTER I WITH DOT ABOVE”字符。这可能会导致意外的结果,例如在Example 1
中,这会阻止此验证捕获“script”一词,从而导致 Cross-Site Scripting 漏洞。
...
import java.sql.PreparedStatement;
import com.sap.sql.NativeSQLAccess;
String mssOnlyStmt = "...";
// variant 1
PreparedStatement ps =
NativeSQLAccess.prepareNativeStatement(
conn, mssOnlyStmt);
. . .
// variant 2
Statement stmt =
NativeSQLAccess.createNativeStatement(conn);
int result = stmt.execute(mssOnlyStmt);
. . .
// variant 3
CallableStatement cs =
NativeSQLAccess.prepareNativeCall(
conn, mssOnlyStmt);
. . .
...
public class Box{
public int area;
public static final int width = 10;
public static final Box box = new Box();
public static final int height = (int) (Math.random() * 100);
public Box(){
area = width * height;
}
...
}
...
Example 1
中,由于 width
等于 10,因此开发人员希望 box.area
是一个随机整数,该整数恰好为 10 的倍数。但在现实情况中,它可能始终具有硬编码值 0。系统首先会对使用编译时常量声明的静态最终字段进行初始化,然后依次执行各个代码。这意味着,由于 height
不是编译时常量,因此会在声明 box
后对其进行声明,这样就会在初始化 height
字段之前调用构造函数。
...
class Foo{
public static final int f = Bar.b - 1;
...
}
...
class Bar{
public static final int b = Foo.f + 1;
...
}
This example is perhaps easier to identify, but would be dependent on which class is loaded first by the JVM. In this exampleFoo.f
could be either -1 or 0, andBar.b
could be either 0 or 1.
null
的指针是否为 null
之前间接引用该指针,则会发生 check-after-dereference 错误。如果程序明确检查过 null
,并确定该指针为 null
,但仍继续间接引用该指针,则会出现 dereference-after-check 错误。此类错误通常是由于错别字或程序员疏忽造成的。如果程序明确将指针设置为 null
,但稍后却间接引用该指针,则将出现 dereference-after-store 错误。此错误通常是因为程序员在声明变量时将该变量初始化为 null
所致。foo
为 null
,然后错误地对其进行间接引用。如果在 if
语句中检查 foo
时其为 null
,则会发生 null
间接引用,从而导致 null 指针异常。示例 2:在下列代码中,程序员假设变量
if (foo is null) {
foo.SetBar(val);
...
}
foo
不是 null
,并通过间接引用该对象来确认此假设。但是,程序员稍后通过检查 foo
是否为 null
发现事实与该假设相反。如果在 if
指令中检查时发现 foo
可能是 null
,则在间接引用它时可能也为 null
,并可能引起 null 指针异常。间接引用不安全,或者无需后续检查。示例 3:在下列代码中,程序员会将变量
foo.SetBar(val);
...
if (foo is not null) {
...
}
foo
明确设置为 null
。之后,程序员会间接引用 foo
,而未检查对象是否为 null
值。
Foo foo = null;
...
foo.SetBar(val);
...
}
null
的指针是否为 null
之前间接引用该指针,则会发生 check-after-dereference 错误。如果程序明确检查过 null
,并确定该指针为 null
,但仍继续间接引用该指针,则会出现 dereference-after-check 错误。此类错误通常是由于错别字或程序员疏忽造成的。如果程序明确将指针设置为 null
,但稍后却间接引用该指针,则将出现 dereference-after-store 错误。此错误通常是因为程序员在声明变量时将该变量初始化为 null
所致。ptr
不是 NULL
。当程序员间接引用该指针时,这个假设就会清晰的体现出来。当程序员检查 ptr
是否为 NULL
时,就会与该假设发生矛盾。当在 if
语句中检查时,如果 ptr
可以为 NULL
,则在其间接引用时也将为 NULL
,并引起 segmentation fault。示例 2:在下列代码中,程序员会确认变量
ptr->field = val;
...
if (ptr != NULL) {
...
}
ptr
为 NULL
,然后错误地对其进行间接引用。如果在 if
语句中检查 ptr
时其为 NULL
,则会发生 null
dereference,从而导致分段故障。示例 3:在下列代码中,程序员忘记了字符串
if (ptr == null) {
ptr->field = val;
...
}
'\0'
实际上为 0 还是 NULL
,从而间接引用 null 指针并引发分段故障。示例 4:在下列代码中,程序员会将变量
if (ptr == '\0') {
*ptr = val;
...
}
ptr
明确设置为 NULL
。之后,程序员会间接引用 ptr
,而未检查对象是否为 null
值。
*ptr = NULL;
...
ptr->field = val;
...
}
null
,并确定该指针为 null
,但仍继续间接引用该对象,则会出现 dereference-after-check 错误。此类错误通常是由于错别字或程序员疏忽造成的。foo
为 null
,然后错误地对其进行间接引用。如果在 if
语句中检查 foo
时其为 null
,则会发生 null
dereference,从而导致 null 指针异常。
if (foo == null) {
foo.setBar(val);
...
}
int
的 unsigned char
,但返回值将赋给 char
类型。EOF
区别其值。EOF
进行比较。
char c;
while ( (c = getchar()) != '\n' && c != EOF ) {
...
}
getchar()
的返回值将转换为 char
并与 EOF
(一个 int
)进行比较。假设 c
是一个带符号的 8 位值,EOF
是一个带符号的 32 位值,那么如果 getchar()
返回由 0xFF 表示的字符,则与 EOF
相比,c
的值将是 0xFFFFFFFF 的符号扩展。由于 EOF
通常定义为 -1 (0xFFFFFFFF),因此该循环将错误地终止。amount
在返回时可能包含负值。由于函数已声明为返回不带符号的整数,因此 amount
将隐式转换为无符号的值。
unsigned int readdata () {
int amount = 0;
...
if (result == ERROR)
amount = -1;
...
return amount;
}
Example 1
中的错误条件,则 readdata()
的返回值在使用 32 位整型的系统上将为 4,294,967,295。accecssmainframe()
的返回值,变量 amount
在返回时可能包含负值。由于函数已声明为返回不带符号的值,因此 amount
将隐式强制转换为无符号的数字。
unsigned int readdata () {
int amount = 0;
...
amount = accessmainframe();
...
return amount;
}
accessmainframe()
的返回值为 -1,则 readdata()
的返回值在一个 32 位整型系统上将为 4,294,967,295。1
必须传递给第一个参数(版本号):
__xmknod
2
必须传递给第三个参数(组参数):
__wcstod_internal
__wcstof_internal
_wcstol_internal
__wcstold_internal
__wcstoul_internal
3
必须作为第一参数(版本号)进行传递:
__xstat
__lxstat
__fxstat
__xstat64
__lxstat64
__fxstat64
FILE *sysfile = fopen(test.file, "w+");
FILE insecureFile = *sysfile;
sysfile
在 insecureFile
的赋值中被取消引用,使用 insecureFile
会导致各种各样的问题。
FILE *sysfile = fopen(test.file, "r+");
res = fclose(sysfile);
if(res == 0){
printf("%c", getc(sysfile));
}
getc()
函数在 sysfile
文件流关闭之后运行,getc()
导致未定义的行为,并可能导致系统崩溃,或者可能导致修改或读取相同或不同的文件。
std::auto_ptr<foo> p(new foo);
foo* rawFoo = p.get();
delete rawFoo;
delete
之前将指针从管理类中分离出来,则管理类知道以后不再使用该指针。int a = (Int32)i + (Int32)j;
会抛出未处理的异常,并造成应用程序在运行时崩溃。
class Program
{
static int? i = j;
static int? j;
static void Main(string[] args)
{
j = 100;
int a = (Int32)i + (Int32)j;
Console.WriteLine(i);
Console.WriteLine(j);
Console.WriteLine(a);
}
}
aN
和 bN
的值,但在默认情况下,程序员意外地设置了两次 aN
的值。
switch (ctl) {
case -1:
aN = 0; bN = 0;
break;
case 0:
aN = i; bN = -i;
break;
case 1:
aN = i + NEXT_SZ; bN = i - NEXT_SZ;
break;
default:
aN = -1; aN = -1;
break;
}
StreamReader
中的 Finalize()
方法最终会调用 Close()
,但是不能确定何时会调用 Finalize()
方法。事实上,无法确保 Finalize()
会被调用。因此,在繁忙的环境中,这可能会导致 VM 用尽它所有的文件句柄。示例 2:在正常条件下,下面的代码会执行一条数据库查询语句,处理数据库返回的结果,并关闭已分配的
private void processFile(string fName) {
StreamWriter sw = new StreamWriter(fName);
string line;
while ((line = sr.ReadLine()) != null)
processLine(line);
}
SqlConnection
对象。但是如果在执行 SQL 或者处理查询结果时发生异常,那么 SqlConnection
对象将不会被关闭。如果这种情况频繁出现,数据库将用完所有可用的指针,且不能再执行任何 SQL 查询。
...
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(queryString);
cmd.Connection = conn;
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
HarvestResults(rdr);
conn.Connection.Close();
...
int decodeFile(char* fName)
{
char buf[BUF_SZ];
FILE* f = fopen(fName, "r");
if (!f) {
printf("cannot open %s\n", fName);
return DECODE_FAIL;
} else {
while (fgets(buf, BUF_SZ, f)) {
if (!checkChecksum(buf)) {
return DECODE_FAIL;
} else {
decodeBlock(buf);
}
}
}
fclose(f);
return DECODE_SUCCESS;
}
CALL "CBL_CREATE_FILE"
USING filename
access-mode
deny-mode
device
file-handle
END-CALL
IF return-code NOT = 0
DISPLAY "Error!"
GOBACK
ELSE
PERFORM write-data
IF ws-status-code NOT = 0
DISPLAY "Error!"
GOBACK
ELSE
DISPLAY "Success!"
END-IF
END-IF
CALL "CBL_CLOSE_FILE"
USING file-handle
END-CALL
GOBACK
.
New()
函数与系统日志守护进程建立新的连接。它是 log.syslog 程序包的一部分。每次写入返回的写入程序都会发送具有给定优先级(系统日志工具和严重性的组合)和前缀标签的日志消息。在繁忙的环境中,这会导致系统耗尽其所有套接字。示例 2:在此示例中,
func TestNew() {
s, err := New(syslog.LOG_INFO|syslog.LOG_USER, "the_tag")
if err != nil {
if err.Error() == "Unix syslog delivery error" {
fmt.Println("skipping: syslogd not running")
}
fmt.Println("New() failed: %s", err)
}
}
net/smtp
程序包的 Dial()
方法会返回连接到本地主机的 SMTP 服务器的新客户端。连接资源被分配,但从不会通过调用 Close()
功能来释放。
func testDial() {
client, _ := smtp.Dial("127.0.0.1")
client.Hello("")
}
Arena.ofConfined()
创建的资源未关闭。
...
Arena offHeap = Arena.ofConfined()
MemorySegment str = offHeap.allocateUtf8String("data");
...
//offHeap is never closed
BEGIN
...
F1 := UTL_FILE.FOPEN('user_dir','u12345.tmp','R',256);
UTL_FILE.GET_LINE(F1,V1,32767);
...
END;
onPause()
、onStop()
或 onDestroy()
事件处理器中释放 Camera
实例。onPause()
、onStop()
或 onDestroy()
回调中未释放的 Camera
实例。 当 Android OS 需要将当前活动发送到后台或在系统资源不足的情况下需要暂时中断活动时,会调用这些回调。 如果未能正确释放 Camera
对象,该活动会阻止其他应用程序(或者同一应用程序的未来实例)访问照相机。 另外,活动暂停时仍占用 Camera
实例会导致不必要的电池电量消耗,从而对用户体验造成负面影响。Camera
对象的基本 onPause()
方法,也没有在关机顺序中正确释放该对象。
public class UnreleasedCameraActivity extends Activity {
private Camera cam;
@Override
public void onCreate(Bundle state) {
...
}
@Override
public void onRestart() {
...
}
@Override
public void onStop() {
cam.stopPreview();
}
}
onPause()
、onStop()
或 onDestroy()
事件处理器中释放 MediaRecorder
、MediaPlayer
或 AudioRecord
对象。onPause()
、onStop()
或 onDestroy()
回调中未释放的媒体对象。当 Android OS 需要将当前活动发送到后台或在系统资源不足时暂时中断活动时,会调用这些回调函数。由于未能正确释放媒体对象,该活动导致(其他应用程序或者同一应用程序)随后对 Android 媒体硬件的访问转而依靠软件来实现,甚至彻底失败。打开过多未释放的媒体实例可导致 Android 抛出异常,最终导致 denial of service。另外,活动暂停时仍占用媒体实例会导致不必要的电池电量消耗,从而对用户体验造成负面影响。onPause()
方法,也没有在关机顺序中正确释放该对象。
public class UnreleasedMediaActivity extends Activity {
private MediaPlayer mp;
@Override
public void onCreate(Bundle state) {
...
}
@Override
public void onRestart() {
...
}
@Override
public void onStop() {
mp.stop();
}
}
onPause()
、onStop()
或 onDestroy()
事件处理器中释放 Android 数据库处理器。onPause()
、onStop()
或 onDestroy()
回调中未关闭的 Android SQLite 数据库处理器。当 Android OS 需要将当前活动发送到后台或在系统资源不足时暂时中断活动时,会调用这些回调函数。由于未能正确关闭数据库,如果活动不断重启,则可能会耗尽可用的游标。此外,根据实现方法不同,Android 操作系统也可能抛出异常 DatabaseObjectNotClosedException
。如果未能捕获此异常,应用程序将会崩溃。onPause()
方法,也没有在关机顺序中正确释放该对象。
public class MyDBHelper extends SQLiteOpenHelper {
...
}
public class UnreleasedDBActivity extends Activity {
private myDBHelper dbHelper;
private SQLiteDatabase db;
@Override
public void onCreate(Bundle state) {
...
db = dbHelper.getWritableDatabase();
...
}
@Override
public void onRestart() {
...
}
@Override
public void onStop() {
db.insert(cached_data); // flush cached data
}
}
PWD_COMPARE
程序可被无权访问 sys.dba_users
的代码用来查看用户密码。
CREATE or REPLACE procedure PWD_COMPARE(p_user VARCHAR, p_pwd VARCHAR)
AUTHID DEFINED
IS
cursor INTEGER;
...
BEGIN
IF p_user != 'SYS' THEN
cursor := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cursor, 'SELECT password FROM SYS.DBA_USERS WHERE username = :u', DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE(cursor, ':u', p_user);
...
END IF;
END PWD_COMPARE;
sys
密码。造成异常的一种方式为将过长的参数传递到 p_user
。在攻击者了解到指针已泄漏之后,他们仅需猜测指针并分配新的绑定变量。
DECLARE
x VARCHAR(32000);
i INTEGER;
j INTEGER;
r INTEGER;
password VARCHAR2(30);
BEGIN
FOR i IN 1..10000 LOOP
x:='b' || x;
END LOOP;
SYS.PWD_COMPARE(x,'password');
EXCEPTION WHEN OTHERs THEN
FOR j IN 1..10000
DBMS_SQL.BIND_VARIABLE(j, ':u', 'SYS');
DBMS_SQL.DEFINE_COLUMN(j, 1, password, 30);
r := DBMS_SQL.EXECUTE(j);
IF DBMS_SQL.FETCH_ROWS(j) > 0 THEN
DBMS_SQL.COLUMN_VALUE(j, 1, password);
EXIT;
END IF;
END LOOP;
...
END;
DATA: result TYPE demo_update,
request TYPE REF TO IF_HTTP_REQUEST,
obj TYPE REF TO CL_SQL_CONNECTION.
TRY.
...
obj = cl_sql_connection=>get_connection( `R/3*my_conn`).
FINAL(sql) = NEW cl_sql_prepared_statement(
statement = `INSERT INTO demo_update VALUES( ?, ?, ?, ?, ?, ? )`).
CATCH cx_sql_exception INTO FINAL(exc).
...
ENDTRY.
SqlConnection
对象。但如果在执行 SQL 或处理结果时发生异常,SqlConnection
对象将不会关闭。如果这种情况频繁出现,数据库将用完所有可用的指针,且不能再执行任何 SQL 查询。
...
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(queryString);
cmd.Connection = conn;
conn.Open();
SqlDataReader rdr = cmd.ExecuteReader();
HarvestResults(rdr);
conn.Connection.Close();
...
- void insertUser:(NSString *)name {
...
sqlite3_stmt *insertStatement = nil;
NSString *insertSQL = [NSString stringWithFormat:@INSERT INTO users (name, age) VALUES (?, ?)];
const char *insert_stmt = [insertSQL UTF8String];
...
if ((result = sqlite3_prepare_v2(database, insert_stmt,-1, &insertStatement, NULL)) != SQLITE_OK) {
MyLog(@"%s: sqlite3_prepare error: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), result);
return;
}
if ((result = sqlite3_step(insertStatement)) != SQLITE_DONE) {
MyLog(@"%s: step error: %s (%d)", __FUNCTION__, sqlite3_errmsg(database), result);
return;
}
...
}
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(CXN_SQL);
harvestResults(rs);
stmt.close();
func insertUser(name:String, age:int) {
let dbPath = URL(fileURLWithPath: Bundle.main.resourcePath ?? "").appendingPathComponent("test.sqlite").absoluteString
var db: OpaquePointer?
var stmt: OpaquePointer?
if sqlite3_open(dbPath, &db) != SQLITE_OK {
print("Error opening articles database.")
return
}
let queryString = "INSERT INTO users (name, age) VALUES (?,?)"
if sqlite3_prepare(db, queryString, -1, &stmt, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("error preparing insert: \(errmsg)")
return
}
if sqlite3_bind_text(stmt, 1, name, -1, nil) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("failure binding name: \(errmsg)")
return
}
if sqlite3_bind_int(stmt, 2, age) != SQLITE_OK{
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("failure binding name: \(errmsg)")
return
}
if sqlite3_step(stmt) != SQLITE_DONE {
let errmsg = String(cString: sqlite3_errmsg(db)!)
log("failure inserting user: \(errmsg)")
return
}
}
ZipFile
中的 finalize()
方法最终会调用 close()
,但是不能确定何时会调用 finalize()
方法。在繁忙的环境中,这会导致 JVM 用尽它所有的文件句柄。例 2:正常情况下,以下修复代码会在输出所有 zip 文件条目之后正常关闭文件句柄。如果迭代这些条目时出现异常,则不会关闭 zip 文件句柄。如果这种情况经常出现,JVM 就可能耗尽所有可用的文件句柄。
public void printZipContents(String fName) throws ZipException, IOException, SecurityException, IllegalStateException, NoSuchElementException {
ZipFile zf = new ZipFile(fName);
Enumeration<ZipEntry> e = zf.entries();
while (e.hasMoreElements()) {
printFileInfo(e.nextElement());
}
}
public void printZipContents(String fName) throws ZipException, IOException, SecurityException, IllegalStateException, NoSuchElementException {
ZipFile zf = new ZipFile(fName);
Enumeration<ZipEntry> e = zf.entries();
while (e.hasMoreElements()) {
printFileInfo(e.nextElement());
}
zf.close();
}