=== modified file 'src/com/mysql/jdbc/PreparedStatement.java' --- src/com/mysql/jdbc/PreparedStatement.java 2009-01-23 18:21:18 +0000 +++ src/com/mysql/jdbc/PreparedStatement.java 2009-02-23 22:02:13 +0000 @@ -36,6 +36,10 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.net.URL; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; import java.sql.Array; import java.sql.Clob; import java.sql.Date; @@ -54,10 +58,8 @@ import java.util.Locale; import java.util.TimeZone; -import com.mysql.jdbc.exceptions.DeadlockTimeoutRollbackMarker; import com.mysql.jdbc.exceptions.MySQLStatementCancelledException; import com.mysql.jdbc.exceptions.MySQLTimeoutException; -import com.mysql.jdbc.exceptions.MySQLTransactionRollbackException; import com.mysql.jdbc.profiler.ProfilerEvent; /** @@ -528,6 +530,9 @@ private boolean compensateForOnDuplicateKeyUpdate = false; + /** Charset encoder used to test if escaping is needed, such as Yen sign in SJIS */ + private CharsetEncoder charsetEncoder; + /** * Creates a prepared statement instance -- We need to provide factory-style * methods so we can support both JDBC3 (and older) and JDBC4 runtimes, @@ -638,6 +643,8 @@ initializeFromParseInfo(); this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts(); + + charsetEncoder = getEscapingCharsetEncoder(conn.getEncoding()); } /** @@ -677,6 +684,39 @@ initializeFromParseInfo(); this.compensateForOnDuplicateKeyUpdate = this.connection.getCompensateOnDuplicateKeyUpdateCounts(); + + charsetEncoder = getEscapingCharsetEncoder(conn.getEncoding()); + } + + /** + * Get a CharsetEncoder if one is needed for escaping codepoints that are + * transformed to backslash (0x5c) in the given encoding. + * @param encoding The destination encoding + * @return A CharsetEncoder if applicable, null otherwise. + */ + private CharsetEncoder getEscapingCharsetEncoder(String encoding) { + CharsetEncoder enc = Charset.forName(encoding).newEncoder(); + CharBuffer cbuf = CharBuffer.allocate(1); + ByteBuffer bbuf = ByteBuffer.allocate(1); + + cbuf.put("\u00a5"); + cbuf.position(0); + enc.encode(cbuf, bbuf, true); + if(bbuf.get(0) == '\\') { + return enc; + } + + cbuf.clear(); + bbuf.clear(); + + cbuf.put("\u20a9"); + cbuf.position(0); + enc.encode(cbuf, bbuf, true); + if(bbuf.get(0) == '\\') { + return enc; + } + + return null; } /** @@ -4131,6 +4171,20 @@ break; + case '\u00a5': + case '\u20a9': + // escape characters interpreted as backslash by mysql + if(charsetEncoder != null) { + CharBuffer cbuf = CharBuffer.allocate(1); + ByteBuffer bbuf = ByteBuffer.allocate(1); + cbuf.put(c); + cbuf.position(0); + charsetEncoder.encode(cbuf, bbuf, true); + if(bbuf.get(0) == '\\') { + buf.append('\\'); + } + } + // fall through default: buf.append(c); } === modified file 'src/testsuite/regression/StatementRegressionTest.java' --- src/testsuite/regression/StatementRegressionTest.java 2009-02-02 17:00:13 +0000 +++ src/testsuite/regression/StatementRegressionTest.java 2009-02-22 23:11:19 +0000 @@ -5825,4 +5825,28 @@ closeMemberJDBCResources(); } } + + /** + * Bug #41730 - SQL Injection when using U+00A5 and SJIS/Windows-31J + */ + public void testBug41730() throws Exception { + Connection conn2 = null; + PreparedStatement pstmt2 = null; + try { + conn2 = getConnectionWithProps("characterEncoding=sjis"); + pstmt2 = conn2.prepareStatement("select ?"); + pstmt2.setString(1, "\u00A5'"); + // this will throw an exception with a syntax error if it fails + pstmt2.executeQuery(); + } finally { + try { + if(pstmt2 != null) + pstmt2.close(); + } catch(SQLException ex) {} + try { + if(conn2 != null) + conn2.close(); + } catch(SQLException ex) {} + } + } } \ No newline at end of file