কারণ eval
এবং exec
এত বিপজ্জনক হ'ল ডিফল্ট compile
ফাংশনটি কোনও বৈধ পাইথন অভিব্যক্তির জন্য বাইকোড এবং ডিফল্ট eval
বাexec
কোন বৈধ পাইথন বাইটকোড চালানো হবে। তারিখের সমস্ত উত্তরগুলি এএসটি ব্যবহার করে তৈরি করা যেতে পারে (ইনপুট স্যানিটাইজ করে) বা বাইকোডকে সীমাবদ্ধ করার উপর দৃষ্টি নিবদ্ধ করে বা আপনার নিজস্ব ডোমেন-নির্দিষ্ট-ভাষা তৈরি করতে।
পরিবর্তে, আপনি সহজেই একটি সাধারণ eval
ক্রিয়াকলাপ তৈরি করতে পারেন যা খারাপ কিছু করতে অক্ষম এবং সহজেই ব্যবহৃত স্মৃতি বা সময়টিতে রানটাইম চেক করতে পারে। অবশ্যই এটি শর্টকার্টের চেয়ে সহজ গণিত যদি হয়।
c = compile(stringExp, 'userinput', 'eval')
if c.co_code[0]==b'd' and c.co_code[3]==b'S':
return c.co_consts[ord(c.co_code[1])+ord(c.co_code[2])*256]
এটি যেভাবে কাজ করে তা সহজ, যে কোনও ধ্রুবিত গাণিতিক অভিব্যক্তি সংকলনের সময় নিরাপদে মূল্যায়ন করা হয় এবং ধ্রুবক হিসাবে সংরক্ষণ করা হয়। সংকলন দ্বারা প্রত্যাবর্তিত কোড অবজেক্টটি গঠিত হয় d
, যার জন্য বাইটোকড হয় LOAD_CONST
, তারপরে লোড করার ধ্রুবকের সংখ্যা থাকে (সাধারণত তালিকার সর্বশেষে শেষ হয়), তারপরে থাকে S
, যা বাইটোকড হয়RETURN_VALUE
। যদি এই শর্টকাটটি কাজ না করে, এর অর্থ হ'ল ব্যবহারকারীর ইনপুটটি একটি ধ্রুবক অভিব্যক্তি নয় (এতে একটি ভেরিয়েবল বা ফাংশন কল বা অনুরূপ থাকে)।
এটি আরও কিছু পরিশীলিত ইনপুট ফর্ম্যাটগুলির দরজা উন্মুক্ত করে। উদাহরণ স্বরূপ:
stringExp = "1 + cos(2)"
এটিতে আসলে বাইটকোড মূল্যায়ন করা দরকার যা এখনও বেশ সহজ। পাইথন বাইটকোড হ'ল স্ট্যাক ওরিয়েন্টেড ভাষা, তাই সবকিছুই সাধারণ TOS=stack.pop(); op(TOS); stack.put(TOS)
বা অনুরূপ। কীটি কেবলমাত্র ওপকডগুলি নিরাপদ (লোডিং / স্টোরেজ মান, গণিত ক্রিয়াকলাপ, প্রত্যাবর্তন মান) এবং অনিরাপদ নয় (অ্যাট্রিবিউট লুকআপ) প্রয়োগ করে implement যদি আপনি চান যে ব্যবহারকারী ফাংশনগুলিতে কল করতে সক্ষম হবেন (উপরের শর্টকাটটি ব্যবহার না করার পুরো কারণ), সহজভাবে আপনার কার্যকরকরণটিকে CALL_FUNCTION
কেবল একটি 'নিরাপদ' তালিকায় অনুমতি দিন।
from dis import opmap
from Queue import LifoQueue
from math import sin,cos
import operator
globs = {'sin':sin, 'cos':cos}
safe = globs.values()
stack = LifoQueue()
class BINARY(object):
def __init__(self, operator):
self.op=operator
def __call__(self, context):
stack.put(self.op(stack.get(),stack.get()))
class UNARY(object):
def __init__(self, operator):
self.op=operator
def __call__(self, context):
stack.put(self.op(stack.get()))
def CALL_FUNCTION(context, arg):
argc = arg[0]+arg[1]*256
args = [stack.get() for i in range(argc)]
func = stack.get()
if func not in safe:
raise TypeError("Function %r now allowed"%func)
stack.put(func(*args))
def LOAD_CONST(context, arg):
cons = arg[0]+arg[1]*256
stack.put(context['code'].co_consts[cons])
def LOAD_NAME(context, arg):
name_num = arg[0]+arg[1]*256
name = context['code'].co_names[name_num]
if name in context['locals']:
stack.put(context['locals'][name])
else:
stack.put(context['globals'][name])
def RETURN_VALUE(context):
return stack.get()
opfuncs = {
opmap['BINARY_ADD']: BINARY(operator.add),
opmap['UNARY_INVERT']: UNARY(operator.invert),
opmap['CALL_FUNCTION']: CALL_FUNCTION,
opmap['LOAD_CONST']: LOAD_CONST,
opmap['LOAD_NAME']: LOAD_NAME
opmap['RETURN_VALUE']: RETURN_VALUE,
}
def VMeval(c):
context = dict(locals={}, globals=globs, code=c)
bci = iter(c.co_code)
for bytecode in bci:
func = opfuncs[ord(bytecode)]
if func.func_code.co_argcount==1:
ret = func(context)
else:
args = ord(bci.next()), ord(bci.next())
ret = func(context, args)
if ret:
return ret
def evaluate(expr):
return VMeval(compile(expr, 'userinput', 'eval'))
স্পষ্টতই, এর আসল সংস্করণটি কিছুটা দীর্ঘ হবে (এখানে 119 টি অপকড রয়েছে, যার মধ্যে 24 টি গণিত সম্পর্কিত)। যুক্ত করা STORE_FAST
এবং অন্য কয়েকজনকে 'x=5;return x+x
তুচ্ছভাবে সহজেই অনুরূপ বা অনুরূপ ইনপুট দেওয়ার অনুমতি দেওয়া হত । এমনকি এটি ব্যবহারকারী-তৈরি ফাংশনগুলি সম্পাদন করতে ব্যবহার করা যেতে পারে, যতক্ষণ না ব্যবহারকারী তৈরি করা ফাংশনগুলি VMeval এর মাধ্যমে নিজেরাই সম্পাদন করা হয় (তাদেরকে কল করতে পারেন না !!! বা তারা কোথাও কলব্যাক হিসাবে ব্যবহার করতে পারেন)। লুপগুলি পরিচালনা করার জন্য বাইটোকোডগুলির জন্য সমর্থন প্রয়োজন goto
, যার অর্থ একটি for
পুনরাবৃত্তকারী থেকে পরিবর্তন while
এবং বর্তমান নির্দেশে একটি পয়েন্টার বজায় রাখা, তবে খুব বেশি শক্ত নয়। ডসের প্রতিরোধের জন্য, মূল লুপটি গণনা শুরু হওয়ার পরে কতটা সময় কেটে গেছে তা পরীক্ষা করা উচিত এবং কিছু নির্দিষ্ট অপারেটরকে কিছু যুক্তিসঙ্গত সীমাতে ইনপুট অস্বীকার করা উচিত (BINARY_POWER
সর্বাধিক সুস্পষ্ট হওয়া)।
এই অভিব্যক্তিটি সাধারণ অভিব্যক্তির জন্য একটি সাধারণ ব্যাকরণ পার্সারের তুলনায় কিছুটা দীর্ঘ (যদিও উপরের অংশটি কেবল সংকলিত ধ্রুবক ধরার বিষয়ে দেখুন) এটি আরও জটিল ইনপুটটিতে সহজেই প্রসারিত হয় এবং ব্যাকরণের সাথে ডিলের প্রয়োজন হয় না ( compile
যথেচ্ছ জটিলভাবে কিছু নেয় এবং এটিকে হ্রাস করে) সাধারণ নির্দেশাবলীর ক্রম)।